- Notifications
You must be signed in to change notification settings - Fork 422
Feat: Template review #1570
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
base: main
Are you sure you want to change the base?
Feat: Template review #1570
Conversation
WalkthroughAdds WorkspacePage and WorkflowPage exports and routes; replaces audio/background entity ID fields with Changes
Sequence Diagram(s)sequenceDiagram participant User participant Router participant WorkspacePage participant WorkflowPage participant Canvas participant FrontPage participant API participant NotificationService User->>Router: GET /workspace Router->>WorkspacePage: mount() WorkspacePage->>FrontPage: render() FrontPage-->>User: display User->>Router: GET /workflow/:workflowId Router->>WorkflowPage: mount(workflowId) WorkflowPage->>Canvas: render(readonly?) Canvas-->>User: display Note over User,API: Create workflow app (optional publishToCommunity) User->>API: POST /workflow-app { ..., publishToCommunity } API->>API: persist publishToCommunity, set publishReviewStatus, create share(s) alt publishToCommunity && shareId exists API->>NotificationService: send review email (templateLink, note?) NotificationService-->>API: ack end API-->>User: response (includes publishToCommunity) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/src/routes/index.tsx(3 hunks)packages/ai-workspace-common/src/requests/types.gen.ts(2 hunks)packages/request/src/requests/types.gen.ts(2 hunks)packages/web-core/src/index.ts(1 hunks)packages/web-core/src/pages/workflow/index.tsx(1 hunks)packages/web-core/src/pages/workspace/index.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{jsx,tsx}: Always use tailwind css to style the component.
Always wrap pure components with React.memo to prevent unnecessary re-renders.
Always use useMemo for expensive computations or complex object creation.
Always use useCallback for function props to maintain referential equality.
Always specify proper dependency arrays in useEffect to prevent infinite loops.
Always avoid inline object/array creation in render to prevent unnecessary re-renders.
Always use proper key props when rendering lists.
Always split nested components with closures into separate components to avoid performance issues and improve code maintainability.
**/*.{jsx,tsx}: Always wrap pure components with React.memo to prevent unnecessary re-renders
Always use useMemo for expensive computations or complex object creation
Always use useCallback for function props to maintain referential equality
Always specify proper dependency arrays in useEffect to prevent infinite loops
Always avoid inline object/array creation in render to prevent unnecessary re-renders
Always use proper key props when rendering lists (avoid using index when possible)
Always split nested components with closures into separate components
Use lazy loading for components that are not immediately needed
Debounce handlers for events that might fire rapidly (resize, scroll, input)
Implement fallback UI for components that might fail
Use error boundaries to catch and handle runtime errors
Use Tailwind CSS for styling components
Group related utility classes together
Prefer Tailwind utilities over custom CSS when possible
**/*.{jsx,tsx}: Place each attribute on a new line when a component has multiple attributes in JSX
Use self-closing tags for elements without children in JSX
Keep JSX expressions simple, extract complex logic to variables
Put closing brackets for multi-line JSX on a new lineUse React best practices for frontend code
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxapps/web/src/routes/index.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Always use optional chaining (?.) when accessing object properties.
Always use nullish coalescing (??) or default values for potentially undefined values.
Always check array existence before using array methods.
Always validate object properties before destructuring.
Always use single quotes for string literals in JavaScript/TypeScript code.Use JSDoc style comments for functions and classes in JavaScript/TypeScript
**/*.{js,jsx,ts,tsx}: Use single quotes for string literals
Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Validate object properties before destructuring
Use ES6+ features like arrow functions, destructuring, and spread operators
Use async/await instead of raw promises for asynchronous code
**/*.{js,jsx,ts,tsx}: Maximum line length of 100 characters
Use 2 spaces for indentation, no tabs
Use semicolons at the end of statements
Include spaces around operators (e.g.,a + binstead ofa+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement
No trailing whitespace at the end of lines
**/*.{js,jsx,ts,tsx}: Each file should contain only one main component
File names should match the component name
Keep component files focused with minimal dependencies
Group import statements in the following order: 1. React/framework libraries 2. Third-party libraries 3. Internal modules 4. Relative path imports (parent directories first, then child directories) 5. Type imports 6. Style imports
Sort imports alphabetically within each group
Leave a blank line between import groups
Components should be organized in the following order: 1. Import statements 2. Type definitions 3. Constant declarations 4. Component function 5. Hook calls 6. Event handlers 7. Helper render functions 8. JSX return statement
Extract complex logic in...
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxpackages/web-core/src/index.tsapps/web/src/routes/index.tsxpackages/ai-workspace-common/src/requests/types.gen.tspackages/request/src/requests/types.gen.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)
**/*.{ts,tsx}: Avoid usinganytype whenever possible - it defeats the purpose of TypeScript
When tempted to useany, useunknowntype instead and add proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new ones
Use TypeScript's utility types to derive new types:Partial<T>,Pick<T, K>,Omit<T, K>,Readonly<T>,Record<K, T>
Use union types and intersection types to combine existing types
Always import types explicitly using theimport typesyntax
Group type imports separately from value imports
Minimize creating local type aliases for imported typesExplicitly type props interfaces/types
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxpackages/web-core/src/index.tsapps/web/src/routes/index.tsxpackages/ai-workspace-common/src/requests/types.gen.tspackages/request/src/requests/types.gen.ts
**/index.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/05-code-organization.mdc)
Use index files to export multiple components from a directory
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxpackages/web-core/src/index.tsapps/web/src/routes/index.tsx
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}: Use Primary color (#155EEF) as the main brand color for buttons, links, and accents
Use Error color (#F04438) for error states and destructive actions
Use Success color (#12B76A) for success states and confirmations
Use Warning color (#F79009) for warnings and important notifications
Use Info color (#0BA5EC) for informational elements
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxpackages/web-core/src/index.tsapps/web/src/routes/index.tsxpackages/ai-workspace-common/src/requests/types.gen.tspackages/request/src/requests/types.gen.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)
**/*.tsx: Use the translation wrapper component when displaying user-facing text in components
Ensure all user-facing text in components is translatable
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxapps/web/src/routes/index.tsx
**/*.{css,scss,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)
**/*.{css,scss,less,styl,js,jsx,ts,tsx}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Maintain a minimum contrast ratio of 4.5:1 for text
Files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/pages/workflow/index.tsxpackages/web-core/src/index.tsapps/web/src/routes/index.tsxpackages/ai-workspace-common/src/requests/types.gen.tspackages/request/src/requests/types.gen.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)
Follow NestJS patterns for backend code
Files:
packages/web-core/src/index.tspackages/ai-workspace-common/src/requests/types.gen.tspackages/request/src/requests/types.gen.ts
🧠 Learnings (3)
📚 Learning: 2025-07-21T01:56:03.072Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/02-project-structure.mdc:0-0 Timestamp: 2025-07-21T01:56:03.072Z Learning: Shared UI components and utilities are located in packages/ai-workspace-common Applied to files:
packages/web-core/src/pages/workspace/index.tsxpackages/web-core/src/index.ts
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/index.{js,jsx,ts,tsx} : Use index files to export multiple components from a directory Applied to files:
packages/web-core/src/index.ts
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Use lazy loading for components that are not immediately needed Applied to files:
packages/web-core/src/index.ts
🧬 Code graph analysis (3)
packages/web-core/src/pages/workspace/index.tsx (3)
packages/web-core/src/index.ts (1)
WorkspacePage(26-26)packages/telemetry-web/src/index.ts (1)
logEvent(29-39)packages/ai-workspace-common/src/components/canvas/front-page/index.tsx (1)
FrontPage(42-173)
packages/web-core/src/pages/workflow/index.tsx (2)
packages/web-core/src/index.ts (1)
WorkflowPage(25-25)packages/telemetry-web/src/index.ts (1)
logEvent(29-39)
apps/web/src/routes/index.tsx (1)
packages/web-core/src/index.ts (2)
WorkspacePage(26-26)WorkflowPage(25-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (7)
packages/request/src/requests/types.gen.ts (1)
4383-4389: Good refactoring of media reference priorities in HeyGenGenerateVideoRequest.The addition of
storageKeyfields for both audio (line 4389) and background (line 4418) resources properly establishes storage-based references as the preferred option over direct URLs. The updated JSDoc comments now correctly reflect the priority semantics:
- Audio:
storageKeytakes priority overaudioUrl- Background:
storageKeytakes priority overurlThis enables more efficient and reliable media handling in video generation workflows.
Since this file is auto-generated and the AI summary mentions parallel changes in
packages/ai-workspace-common/src/requests/types.gen.ts, please verify that the corresponding payload type refactoring has been applied there as well to maintain consistency across packages.Also applies to: 4412-4418
packages/ai-workspace-common/src/requests/types.gen.ts (1)
1-1: Auto-generated file - no manual review needed.This file is auto-generated by @hey-api/openapi-ts. The type changes (audioEntityId → storageKey and entityId → storageKey) appear to be part of the intended refactoring.
packages/web-core/src/pages/workspace/index.tsx (1)
1-3: LGTM - imports are well organized.The imports follow the project structure with React, internal packages, and telemetry properly grouped.
packages/web-core/src/index.ts (1)
25-26: LGTM - consistent with existing patterns.The new lazy-loaded exports follow the established pattern in the file and support proper code splitting for these SEO-friendly routes.
apps/web/src/routes/index.tsx (2)
19-20: LGTM - imports follow existing pattern.The new page imports are consistent with the existing import structure and naming conventions.
98-106: LGTM - new routes are well-structured.The new SEO-friendly routes follow the existing pattern and correctly use dynamic parameters for the workflow ID.
packages/web-core/src/pages/workflow/index.tsx (1)
1-6: LGTM - imports are well organized.The imports follow the project guidelines with proper grouping: React libraries, third-party libraries, internal modules, and utilities.
| const WorkflowPage = () => { | ||
| const { workflowId = '' } = useParams(); | ||
| const isShareMode = workflowId?.startsWith('can-'); | ||
| console.log({ isShareMode, workflowId }); | ||
| | ||
| const { collapse } = useSiderStoreShallow((state) => ({ | ||
| collapse: state.collapse, | ||
| })); | ||
| | ||
| useEffect(() => { | ||
| if (workflowId) { | ||
| if (isShareMode) { | ||
| logEvent('enter_share_canvas', null, { canvasId: workflowId }); | ||
| } else { | ||
| logEvent('enter_canvas', null, { canvasId: workflowId }); | ||
| } | ||
| } | ||
| }, [workflowId, isShareMode]); | ||
| | ||
| if (isShareMode) { | ||
| return ( | ||
| <div className={cn('w-full h-full flex flex-col p-2', { '!p-0': collapse })}> | ||
| <Canvas canvasId={workflowId} readonly /> | ||
| </div> | ||
| ); | ||
| } | ||
| | ||
| return ( | ||
| <div className="w-full h-full flex flex-col"> | ||
| <Canvas canvasId={workflowId} /> | ||
| </div> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add React.memo, JSDoc, and explicit return type.
The component should follow the project's TypeScript and React guidelines similar to other page components:
- Wrap with React.memo to prevent unnecessary re-renders
- Add JSDoc documentation
- Define explicit return type
As per coding guidelines.
Apply this refactor:
+import type { FC } from 'react'; import { useParams } from 'react-router-dom'; -import { useEffect } from 'react'; +import { useEffect, memo } from 'react'; import { Canvas } from '@refly-packages/ai-workspace-common/components/canvas'; import { useSiderStoreShallow } from '@refly/stores'; import cn from 'classnames'; import { logEvent } from '@refly/telemetry-web'; -const WorkflowPage = () => { +/** + * WorkflowPage component that displays a workflow canvas. + * Supports both regular canvas view and read-only share mode. + * Share mode is determined by the workflowId prefix ('can-'). + */ +const WorkflowPage: FC = memo(() => { const { workflowId = '' } = useParams(); const isShareMode = workflowId?.startsWith('can-'); - console.log({ isShareMode, workflowId }); const { collapse } = useSiderStoreShallow((state) => ({ collapse: state.collapse, })); useEffect(() => { if (workflowId) { if (isShareMode) { logEvent('enter_share_canvas', null, { canvasId: workflowId }); } else { logEvent('enter_canvas', null, { canvasId: workflowId }); } } }, [workflowId, isShareMode]); if (isShareMode) { return ( <div className={cn('w-full h-full flex flex-col p-2', { '!p-0': collapse })}> <Canvas canvasId={workflowId} readonly /> </div> ); } return ( <div className="w-full h-full flex flex-col"> <Canvas canvasId={workflowId} /> </div> ); -}; +}); + +WorkflowPage.displayName = 'WorkflowPage'; export default WorkflowPage;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
packages/web-core/src/pages/workflow/index.tsx around lines 8 to 40: The component lacks JSDoc, an explicit return type, and React.memo wrapping; add a JSDoc block above the component describing its purpose, declare the component with an explicit return type (e.g., (): JSX.Element or React.FC) instead of inferring it, and export the memoized component (wrap with React.memo) to prevent unnecessary re-renders following project conventions. | const WorkflowPage = () => { | ||
| const { workflowId = '' } = useParams(); | ||
| const isShareMode = workflowId?.startsWith('can-'); | ||
| console.log({ isShareMode, workflowId }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statement.
This debug logging should be removed before merging to production. If logging is needed for troubleshooting, consider using the telemetry system or a proper logging framework.
Apply this diff:
const { workflowId = '' } = useParams(); const isShareMode = workflowId?.startsWith('can-'); - console.log({ isShareMode, workflowId });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log({ isShareMode, workflowId }); | |
| const { workflowId = '' } = useParams(); | |
| const isShareMode = workflowId?.startsWith('can-'); |
🤖 Prompt for AI Agents
In packages/web-core/src/pages/workflow/index.tsx at line 11, remove the debug console.log({ isShareMode, workflowId }); statement; if runtime diagnostics are required replace it with a call to the app telemetry/logging utility (e.g., telemetry.info or logger.debug) and ensure no plain console.* calls remain in production code. | const WorkspacePage = () => { | ||
| useEffect(() => { | ||
| logEvent('enter_workspace'); | ||
| }, []); | ||
| | ||
| return ( | ||
| <div className="w-full h-full flex flex-col"> | ||
| <FrontPage /> | ||
| </div> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add React.memo, JSDoc, and explicit return type.
The component should follow the project's TypeScript and React guidelines:
- Wrap with React.memo to prevent unnecessary re-renders
- Add JSDoc documentation
- Define explicit return type
As per coding guidelines.
Apply this refactor:
+import { memo } from 'react'; +import type { FC } from 'react'; import { useEffect } from 'react'; import { FrontPage } from '@refly-packages/ai-workspace-common/components/canvas/front-page'; import { logEvent } from '@refly/telemetry-web'; -const WorkspacePage = () => { +/** + * WorkspacePage component that displays the workspace home/front page. + * Logs telemetry event when the workspace is entered. + */ +const WorkspacePage: FC = memo(() => { useEffect(() => { logEvent('enter_workspace'); }, []); return ( <div className="w-full h-full flex flex-col"> <FrontPage /> </div> ); -}; +}); + +WorkspacePage.displayName = 'WorkspacePage'; export default WorkspacePage;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const WorkspacePage = () => { | |
| useEffect(() => { | |
| logEvent('enter_workspace'); | |
| }, []); | |
| return ( | |
| <div className="w-full h-full flex flex-col"> | |
| <FrontPage /> | |
| </div> | |
| ); | |
| }; | |
| import { memo } from 'react'; | |
| import type { FC } from 'react'; | |
| import { useEffect } from 'react'; | |
| import { FrontPage } from '@refly-packages/ai-workspace-common/components/canvas/front-page'; | |
| import { logEvent } from '@refly/telemetry-web'; | |
| /** | |
| * WorkspacePage component that displays the workspace home/front page. | |
| * Logs telemetry event when the workspace is entered. | |
| */ | |
| const WorkspacePage: FC = memo(() => { | |
| useEffect(() => { | |
| logEvent('enter_workspace'); | |
| }, []); | |
| return ( | |
| <div className="w-full h-full flex flex-col"> | |
| <FrontPage /> | |
| </div> | |
| ); | |
| }); | |
| WorkspacePage.displayName = 'WorkspacePage'; | |
| export default WorkspacePage; |
🤖 Prompt for AI Agents
In packages/web-core/src/pages/workspace/index.tsx around lines 5 to 15, the WorkspacePage component needs to follow project TS/React guidelines: add a JSDoc block describing the component, give the function an explicit return type (e.g., React.ReactElement), and export the component wrapped with React.memo to avoid unnecessary re-renders; ensure React (and ReactElement if used) and memo are imported or referenced, and keep existing useEffect/logEvent logic intact. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/src/routes/index.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{jsx,tsx}: Always use tailwind css to style the component.
Always wrap pure components with React.memo to prevent unnecessary re-renders.
Always use useMemo for expensive computations or complex object creation.
Always use useCallback for function props to maintain referential equality.
Always specify proper dependency arrays in useEffect to prevent infinite loops.
Always avoid inline object/array creation in render to prevent unnecessary re-renders.
Always use proper key props when rendering lists.
Always split nested components with closures into separate components to avoid performance issues and improve code maintainability.
**/*.{jsx,tsx}: Always wrap pure components with React.memo to prevent unnecessary re-renders
Always use useMemo for expensive computations or complex object creation
Always use useCallback for function props to maintain referential equality
Always specify proper dependency arrays in useEffect to prevent infinite loops
Always avoid inline object/array creation in render to prevent unnecessary re-renders
Always use proper key props when rendering lists (avoid using index when possible)
Always split nested components with closures into separate components
Use lazy loading for components that are not immediately needed
Debounce handlers for events that might fire rapidly (resize, scroll, input)
Implement fallback UI for components that might fail
Use error boundaries to catch and handle runtime errors
Use Tailwind CSS for styling components
Group related utility classes together
Prefer Tailwind utilities over custom CSS when possible
**/*.{jsx,tsx}: Place each attribute on a new line when a component has multiple attributes in JSX
Use self-closing tags for elements without children in JSX
Keep JSX expressions simple, extract complex logic to variables
Put closing brackets for multi-line JSX on a new lineUse React best practices for frontend code
Files:
apps/web/src/routes/index.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Always use optional chaining (?.) when accessing object properties.
Always use nullish coalescing (??) or default values for potentially undefined values.
Always check array existence before using array methods.
Always validate object properties before destructuring.
Always use single quotes for string literals in JavaScript/TypeScript code.Use JSDoc style comments for functions and classes in JavaScript/TypeScript
**/*.{js,jsx,ts,tsx}: Use single quotes for string literals
Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Validate object properties before destructuring
Use ES6+ features like arrow functions, destructuring, and spread operators
Use async/await instead of raw promises for asynchronous code
**/*.{js,jsx,ts,tsx}: Maximum line length of 100 characters
Use 2 spaces for indentation, no tabs
Use semicolons at the end of statements
Include spaces around operators (e.g.,a + binstead ofa+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement
No trailing whitespace at the end of lines
**/*.{js,jsx,ts,tsx}: Each file should contain only one main component
File names should match the component name
Keep component files focused with minimal dependencies
Group import statements in the following order: 1. React/framework libraries 2. Third-party libraries 3. Internal modules 4. Relative path imports (parent directories first, then child directories) 5. Type imports 6. Style imports
Sort imports alphabetically within each group
Leave a blank line between import groups
Components should be organized in the following order: 1. Import statements 2. Type definitions 3. Constant declarations 4. Component function 5. Hook calls 6. Event handlers 7. Helper render functions 8. JSX return statement
Extract complex logic in...
Files:
apps/web/src/routes/index.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)
**/*.{ts,tsx}: Avoid usinganytype whenever possible - it defeats the purpose of TypeScript
When tempted to useany, useunknowntype instead and add proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new ones
Use TypeScript's utility types to derive new types:Partial<T>,Pick<T, K>,Omit<T, K>,Readonly<T>,Record<K, T>
Use union types and intersection types to combine existing types
Always import types explicitly using theimport typesyntax
Group type imports separately from value imports
Minimize creating local type aliases for imported typesExplicitly type props interfaces/types
Files:
apps/web/src/routes/index.tsx
**/index.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/05-code-organization.mdc)
Use index files to export multiple components from a directory
Files:
apps/web/src/routes/index.tsx
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}: Use Primary color (#155EEF) as the main brand color for buttons, links, and accents
Use Error color (#F04438) for error states and destructive actions
Use Success color (#12B76A) for success states and confirmations
Use Warning color (#F79009) for warnings and important notifications
Use Info color (#0BA5EC) for informational elements
Files:
apps/web/src/routes/index.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)
**/*.tsx: Use the translation wrapper component when displaying user-facing text in components
Ensure all user-facing text in components is translatable
Files:
apps/web/src/routes/index.tsx
**/*.{css,scss,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)
**/*.{css,scss,less,styl,js,jsx,ts,tsx}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Maintain a minimum contrast ratio of 4.5:1 for text
Files:
apps/web/src/routes/index.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/index.tsx (1)
packages/web-core/src/index.ts (2)
WorkspacePage(26-26)WorkflowPage(25-25)
🔇 Additional comments (1)
apps/web/src/routes/index.tsx (1)
19-20: Suspense boundary verified and properly configured.The imports of
WorkflowPageandWorkspacePageare correctly added. The verification confirms that a Suspense boundary with aLightLoadingfallback is already configured inapps/web/src/App.tsx(wrapping the Routes component), which properly handles loading states for all lazy-loaded components including these new imports.
| import type { RouteObject } from 'react-router-dom'; | ||
| | ||
| export const RoutesList: RouteObject[] = [ | ||
| // TODO: deprecated and navigate to framer page |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement deprecation strategy or track TODOs in issues.
Multiple TODO comments indicate routes should be deprecated, but no deprecation logic is implemented. This creates technical debt without a clear migration path. Consider:
- Creating tracking issues for each deprecation to ensure they're not forgotten
- Implementing redirect logic if routes are truly deprecated
- Adding deprecation warnings in the old components
- Documenting the migration path for users with existing bookmarks
Particularly concerning is line 77 where /canvas/:canvasId should migrate to /workflow/:workflowId, yet both routes are now active with no indication of which should be used.
Would you like me to help create redirect logic or open tracking issues for these deprecations?
Also applies to: 52-52, 57-57, 62-62, 67-67, 72-72, 77-77, 82-82, 87-87
🤖 Prompt for AI Agents
In apps/web/src/routes/index.tsx around lines 26 and specifically lines 52, 57, 62, 67, 72, 77, 82, and 87, several routes are marked TODO for deprecation but no tracking, redirects, warnings, or migration docs exist; create an explicit deprecation plan by (1) opening a tracking issue for each TODO (include current route, target route, timeline, and owner), (2) implement route-level redirect logic for deprecated routes (e.g., redirect /canvas/:canvasId to /workflow/:workflowId preserving params and query), (3) add an in-component console.warn or UI banner on the old route informing users of deprecation and the new route, and (4) update project docs with a migration path and examples for bookmarked links; ensure redirects are opt-in behind a feature flag or 301/302 decision and add tests to cover the redirects and warning display. | // New SEO-optimized routes | ||
| { | ||
| path: '/workspace', | ||
| element: <WorkspacePage />, | ||
| }, | ||
| { | ||
| path: '/workflow/:workflowId', | ||
| element: <WorkflowPage />, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify SEO implementation in page components.
The route definitions are correctly structured. However, the comment mentions "SEO-optimized routes," but SEO features (meta tags, Open Graph tags, structured data, etc.) are typically implemented within the page components themselves, not in route definitions.
Run the following script to verify SEO implementation in the new page components:
🏁 Script executed:
#!/bin/bash # Description: Check for SEO-related implementations in WorkflowPage and WorkspacePage # Search for SEO-related patterns like Helmet, meta tags, og tags, etc. echo "Checking WorkflowPage for SEO features..." rg -n -C3 -P '(Helmet|meta|og:|twitter:|structured data|jsonld|schema\.org)' packages/web-core/src/pages/workflow/ echo -e "\nChecking WorkspacePage for SEO features..." rg -n -C3 -P '(Helmet|meta|og:|twitter:|structured data|jsonld|schema\.org)' packages/web-core/src/pages/workspace/Length of output: 85
🏁 Script executed:
#!/bin/bash # Locate WorkflowPage and WorkspacePage components anywhere in the codebase echo "Searching for WorkflowPage component..." fd -e tsx -e ts -e jsx -e js | xargs rg -l 'export.*WorkflowPage' 2>/dev/null | head -5 echo -e "\nSearching for WorkspacePage component..." fd -e tsx -e ts -e jsx -e js | xargs rg -l 'export.*WorkspacePage' 2>/dev/null | head -5 # Check if those specific paths exist echo -e "\nChecking if directories exist..." test -d "packages/web-core/src/pages/workflow" && echo "workflow dir exists" || echo "workflow dir NOT found" test -d "packages/web-core/src/pages/workspace" && echo "workspace dir exists" || echo "workspace dir NOT found" # Search more broadly for these components echo -e "\nSearching all component definitions..." rg -n 'WorkflowPage|WorkspacePage' --type ts --type tsx | head -20Length of output: 381
🏁 Script executed:
#!/bin/bash # Inspect the WorkflowPage and WorkspacePage components echo "=== WorkflowPage component ===" cat packages/web-core/src/pages/workflow/index.tsx echo -e "\n=== WorkspacePage component ===" cat packages/web-core/src/pages/workspace/index.tsx # Also check if there's a global Helmet or meta setup echo -e "\n=== Checking main app file for Helmet setup ===" fd -e tsx -e ts 'App' | head -3 | xargs cat 2>/dev/nullLength of output: 4061
Remove misleading "SEO-optimized" comment or implement SEO features.
Verification confirms that neither WorkflowPage nor WorkspacePage implements any SEO features (no Helmet, meta tags, Open Graph tags, structured data, or canonical links). The route comment "New SEO-optimized routes" is misleading—these are standard React Router definitions without actual SEO implementation.
Either remove the misleading comment from line 100 or implement proper SEO features in the page components (e.g., using react-helmet-async for dynamic meta tags, Open Graph tags, and structured data).
🤖 Prompt for AI Agents
In apps/web/src/routes/index.tsx around lines 100 to 108, the inline comment "New SEO-optimized routes" is misleading because WorkspacePage and WorkflowPage do not provide any SEO metadata; remove or update the comment to accurately describe these routes, or implement SEO in the respective page components by adding dynamic meta tags (e.g., using react-helmet-async) including title, description, Open Graph tags and canonical link / structured data as appropriate, ensuring the routes' comment reflects the actual behavior. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/openapi-schema/schema.yml (1)
10481-10483: Clarify semantics + add default; consider surfacing read-side status.
- Add an explicit default to avoid client ambiguity.
- Specify whether this is a one-shot create-time flag (triggers publish) or a persisted attribute. If persisted, expose it on WorkflowApp; if one-shot, say so in description and advise clients to check shareId.
Apply default on the request property:
remixEnabled: type: boolean description: Whether remix is enabled for this app publishToCommunity: type: boolean description: Whether to publish this app to the community + default: falseIf persisted, also expose on the response model (example insertion in WorkflowApp properties):
publishToCommunity: type: boolean description: Whether this app is published to the community (server state)Also:
- Update any request/response examples to include publishToCommunity where relevant.
- Regenerate typed clients (packages/request/.../types.gen.ts) and ensure UI wiring matches.
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx (2)
1-16: Fix import grouping/order per guidelinesPlace third‑party imports before internal ones. Move refly-icons up with other external libs.
Apply:
import { memo, useCallback, useEffect, useMemo, useState } from 'react'; -import { Button, Form, Input, message, Modal, Upload, Image, Switch, Spin, Tooltip } from 'antd'; +import { Button, Form, Input, message, Modal, Upload, Image, Switch, Spin, Tooltip } from 'antd'; import { PlusOutlined, LoadingOutlined } from '@ant-design/icons'; import { Trans, useTranslation } from 'react-i18next'; +import { ArrowRight, Checked, Question } from 'refly-icons'; import getClient from '@refly-packages/ai-workspace-common/requests/proxiedRequest'; import { useCanvasContext } from '@refly-packages/ai-workspace-common/context/canvas'; import { copyToClipboard } from '@refly-packages/ai-workspace-common/utils'; import { getShareLink } from '@refly-packages/ai-workspace-common/utils/share'; -import { ArrowRight, Checked, Question } from 'refly-icons'; import type { UploadFile, UploadProps } from 'antd/es/upload/interface'; import { logEvent } from '@refly/telemetry-web';As per coding guidelines
320-323: Avoid shadowing imported copyToClipboard; rename local helperLocal const copyToClipboard shadows the imported util, reducing clarity. Rename or reuse the util.
Apply:
- const copyToClipboard = async () => { + const copyLinkToClipboard = async () => { try { await navigator.clipboard.writeText(workflowAppLink); } catch (error) { @@ - await copyToClipboard(); + await copyLinkToClipboard();Also, the comment says “3000ms” but duration is 2000. Make them consistent.
- duration: 2000, // Auto close after 3000ms + duration: 2000, // Auto close after 2000msAlso applies to: 325-368
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx(6 hunks)packages/i18n/src/en-US/ui.ts(1 hunks)packages/i18n/src/zh-Hans/ui.ts(1 hunks)packages/openapi-schema/schema.yml(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Always use optional chaining (?.) when accessing object properties.
Always use nullish coalescing (??) or default values for potentially undefined values.
Always check array existence before using array methods.
Always validate object properties before destructuring.
Always use single quotes for string literals in JavaScript/TypeScript code.Use JSDoc style comments for functions and classes in JavaScript/TypeScript
**/*.{js,jsx,ts,tsx}: Use single quotes for string literals
Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Validate object properties before destructuring
Use ES6+ features like arrow functions, destructuring, and spread operators
Use async/await instead of raw promises for asynchronous code
**/*.{js,jsx,ts,tsx}: Maximum line length of 100 characters
Use 2 spaces for indentation, no tabs
Use semicolons at the end of statements
Include spaces around operators (e.g.,a + binstead ofa+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement
No trailing whitespace at the end of lines
**/*.{js,jsx,ts,tsx}: Each file should contain only one main component
File names should match the component name
Keep component files focused with minimal dependencies
Group import statements in the following order: 1. React/framework libraries 2. Third-party libraries 3. Internal modules 4. Relative path imports (parent directories first, then child directories) 5. Type imports 6. Style imports
Sort imports alphabetically within each group
Leave a blank line between import groups
Components should be organized in the following order: 1. Import statements 2. Type definitions 3. Constant declarations 4. Component function 5. Hook calls 6. Event handlers 7. Helper render functions 8. JSX return statement
Extract complex logic in...
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)
**/*.{ts,tsx}: Avoid usinganytype whenever possible - it defeats the purpose of TypeScript
When tempted to useany, useunknowntype instead and add proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new ones
Use TypeScript's utility types to derive new types:Partial<T>,Pick<T, K>,Omit<T, K>,Readonly<T>,Record<K, T>
Use union types and intersection types to combine existing types
Always import types explicitly using theimport typesyntax
Group type imports separately from value imports
Minimize creating local type aliases for imported typesExplicitly type props interfaces/types
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)
Follow NestJS patterns for backend code
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.ts
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}: Use Primary color (#155EEF) as the main brand color for buttons, links, and accents
Use Error color (#F04438) for error states and destructive actions
Use Success color (#12B76A) for success states and confirmations
Use Warning color (#F79009) for warnings and important notifications
Use Info color (#0BA5EC) for informational elements
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
packages/i18n/src/{en-US,zh-Hans}/**
📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)
packages/i18n/src/{en-US,zh-Hans}/**: Add new translation keys to both language files: packages/i18n/src/en-US and packages/i18n/src/zh-Hans
Maintain consistency in naming conventions for translation keys
Use descriptive keys that reflect the content in translation files
Group related translation keys together
Use namespaces for different sections of the application in translation keys
Follow a hierarchical structure for nested components in translation keys
Support dynamic content with placeholders in translation strings
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.ts
**/*.{css,scss,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)
**/*.{css,scss,less,styl,js,jsx,ts,tsx}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Maintain a minimum contrast ratio of 4.5:1 for text
Files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{jsx,tsx}: Always use tailwind css to style the component.
Always wrap pure components with React.memo to prevent unnecessary re-renders.
Always use useMemo for expensive computations or complex object creation.
Always use useCallback for function props to maintain referential equality.
Always specify proper dependency arrays in useEffect to prevent infinite loops.
Always avoid inline object/array creation in render to prevent unnecessary re-renders.
Always use proper key props when rendering lists.
Always split nested components with closures into separate components to avoid performance issues and improve code maintainability.
**/*.{jsx,tsx}: Always wrap pure components with React.memo to prevent unnecessary re-renders
Always use useMemo for expensive computations or complex object creation
Always use useCallback for function props to maintain referential equality
Always specify proper dependency arrays in useEffect to prevent infinite loops
Always avoid inline object/array creation in render to prevent unnecessary re-renders
Always use proper key props when rendering lists (avoid using index when possible)
Always split nested components with closures into separate components
Use lazy loading for components that are not immediately needed
Debounce handlers for events that might fire rapidly (resize, scroll, input)
Implement fallback UI for components that might fail
Use error boundaries to catch and handle runtime errors
Use Tailwind CSS for styling components
Group related utility classes together
Prefer Tailwind utilities over custom CSS when possible
**/*.{jsx,tsx}: Place each attribute on a new line when a component has multiple attributes in JSX
Use self-closing tags for elements without children in JSX
Keep JSX expressions simple, extract complex logic to variables
Put closing brackets for multi-line JSX on a new lineUse React best practices for frontend code
Files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)
**/*.tsx: Use the translation wrapper component when displaying user-facing text in components
Ensure all user-facing text in components is translatable
Files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
🧠 Learnings (15)
📚 Learning: 2025-07-21T02:58:52.235Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/09-i18n-guidelines.mdc:0-0 Timestamp: 2025-07-21T02:58:52.235Z Learning: Applies to packages/i18n/src/{en-US,zh-Hans}/** : Add new translation keys to both language files: packages/i18n/src/en-US and packages/i18n/src/zh-Hans Applied to files:
packages/i18n/src/zh-Hans/ui.tspackages/i18n/src/en-US/ui.ts
📚 Learning: 2025-07-21T02:58:52.235Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/09-i18n-guidelines.mdc:0-0 Timestamp: 2025-07-21T02:58:52.235Z Learning: Applies to packages/i18n/src/{en-US,zh-Hans}/** : Use descriptive keys that reflect the content in translation files Applied to files:
packages/i18n/src/zh-Hans/ui.ts
📚 Learning: 2025-07-21T02:58:52.235Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/09-i18n-guidelines.mdc:0-0 Timestamp: 2025-07-21T02:58:52.235Z Learning: Applies to packages/i18n/src/{en-US,zh-Hans}/** : Group related translation keys together Applied to files:
packages/i18n/src/zh-Hans/ui.ts
📚 Learning: 2025-07-21T02:58:52.235Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/09-i18n-guidelines.mdc:0-0 Timestamp: 2025-07-21T02:58:52.235Z Learning: Applies to packages/i18n/src/{en-US,zh-Hans}/** : Maintain consistency in naming conventions for translation keys Applied to files:
packages/i18n/src/zh-Hans/ui.ts
📚 Learning: 2025-07-10T02:52:10.899Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/08-contributing-guidelines.mdc:0-0 Timestamp: 2025-07-10T02:52:10.899Z Learning: Applies to **/*.{jsx,tsx} : Use React best practices for frontend code Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-10T02:52:43.495Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/10-testing-guidelines.mdc:0-0 Timestamp: 2025-07-10T02:52:43.495Z Learning: Applies to **/*.{test,spec}.{jsx,tsx} : Use test utilities from React Testing Library Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Extract complex logic into custom hooks Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Always use useCallback for function props to maintain referential equality Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:21.585Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursorrules:0-0 Timestamp: 2025-07-21T01:55:21.585Z Learning: Applies to **/*.{jsx,tsx} : Always use useCallback for function props to maintain referential equality. Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Group related utility classes together Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Use React state hooks for component internal state Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Button*.{js,jsx,ts,tsx} : Include appropriate loading states for async actions Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Implement fallback UI for components that might fail Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Extract repeated JSX patterns into reusable components Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Form*.{js,jsx,ts,tsx,css,scss,less,styl} : Group related form elements with appropriate spacing Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (4)
packages/i18n/src/en-US/ui.ts (1)
3757-3760: English i18n for publishToCommunity addedStructure and copy align with zh-Hans and UI usage. LGTM.
Based on learnings
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx (2)
299-305: API payload props extension looks goodType and defaults for publishToCommunity are clear. LGTM.
411-412: Form defaults and edit-path population for publishToCommunityDefault false on create and fallback on edit are correct. LGTM.
Also applies to: 433-434
packages/i18n/src/zh-Hans/ui.ts (1)
3491-3494: New key added correctly for community publishing — Verified thatworkflowApp.publishToCommunityexists in both locale files with consistent, descriptive translations. Key is properly used in create-modal.tsx and follows naming conventions.
| {/* Publish to Community Switch */} | ||
| <div className="w-full mb-4"> | ||
| <div className="flex items-center justify-between"> | ||
| <div className="flex items-center gap-1"> | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="16" | ||
| height="16" | ||
| viewBox="0 0 16 16" | ||
| fill="none" | ||
| > | ||
| <path | ||
| d="M7.55912 1.62549C8.81738 1.46239 9.83842 2.09438 10.2856 3.17257C10.421 3.49907 10.266 3.87363 9.93953 4.00902C9.62322 4.14013 9.26194 3.99881 9.11661 3.69298L9.10328 3.66298L9.0812 3.61215C8.84594 3.09528 8.38206 2.80894 7.72245 2.89486L7.72016 2.89528L4.24871 3.33548L4.2485 3.33528C3.79774 3.39344 3.49984 3.58977 3.32038 3.84007C3.14817 4.08025 3.04912 4.42147 3.08788 4.8509L3.09746 4.93798L3.09767 4.93944L3.92308 11.4048L3.93621 11.4923C4.0089 11.9217 4.19158 12.2271 4.41788 12.415C4.65329 12.6106 4.98944 12.7244 5.44079 12.6669L6.76641 12.4919C7.11682 12.4457 7.43828 12.6923 7.48453 13.0428C7.53079 13.3932 7.28432 13.7148 6.93391 13.7611L5.60829 13.9359L5.606 13.9363C4.84118 14.0345 4.1384 13.8471 3.59975 13.3996C3.06669 12.9568 2.74903 12.3053 2.65371 11.5692L2.6535 11.5682L1.82788 5.10152L1.82809 5.10131C1.73291 4.36702 1.87656 3.65716 2.28017 3.09423C2.68819 2.52518 3.32098 2.16403 4.08683 2.0657L4.08767 2.06549L7.55912 1.62549Z" | ||
| fill="var(--refly-text-1)" | ||
| /> | ||
| <path | ||
| d="M6.95485 3.26692C7.46902 2.84004 8.13883 2.66074 8.86339 2.7538L8.86318 2.75401L12.1178 3.17109C12.8435 3.26404 13.4466 3.60615 13.8363 4.14942C14.2219 4.68699 14.3581 5.36425 14.2684 6.06358L13.4919 12.119C13.4023 12.8184 13.1 13.4395 12.5913 13.8625C12.0771 14.2901 11.4071 14.4689 10.6813 14.3761L7.42631 13.9581C6.70078 13.8652 6.09776 13.5231 5.70819 12.9798C5.32276 12.4423 5.18692 11.7652 5.27652 11.0659L6.05236 5.01025L6.07173 4.88005C6.18094 4.23344 6.47773 3.66307 6.95485 3.26692ZM8.70027 4.02338C8.29043 3.97074 7.98566 4.07482 7.77256 4.25171C7.5542 4.43301 7.37789 4.73686 7.32194 5.17317L6.54611 11.2286C6.49005 11.6661 6.58361 12.0042 6.7484 12.234C6.90906 12.4581 7.17708 12.6358 7.58902 12.6886L10.8436 13.1063C11.256 13.159 11.5606 13.0547 11.7728 12.8784C11.9903 12.6974 12.1663 12.3938 12.2223 11.9563L12.9988 5.90067C13.0548 5.46364 12.9613 5.12549 12.7963 4.89546C12.6354 4.67117 12.3671 4.49344 11.9553 4.44067L8.70027 4.02338ZM9.81297 10.3852C9.60536 10.4518 9.37814 10.408 9.21006 10.2692L9.61776 9.77566L9.81297 10.3852ZM9.21443 6.04046C9.59681 5.99569 9.97786 6.08668 10.2955 6.28942C10.6318 6.22751 10.982 6.27004 11.2965 6.41671L11.3848 6.46129L11.3886 6.46338C12.3301 6.98096 12.3899 8.0898 11.9878 8.82358L11.9876 8.82337C11.6706 9.40579 11.0772 9.79396 10.6565 10.019C10.4324 10.1389 10.2256 10.2288 10.0748 10.289C9.99905 10.3193 9.93631 10.3424 9.89131 10.3584C9.86878 10.3664 9.85058 10.3727 9.83735 10.3771C9.83075 10.3793 9.82539 10.3812 9.82131 10.3825C9.81926 10.3832 9.81749 10.3837 9.8161 10.3842L9.81297 10.3852L9.61776 9.77566L9.20985 10.269L9.20943 10.2688C9.20926 10.2686 9.20921 10.2683 9.20902 10.2682C9.20858 10.2678 9.20791 10.2674 9.20735 10.2669C9.20623 10.266 9.20479 10.2649 9.20318 10.2636C9.19988 10.2608 9.19557 10.2571 9.19027 10.2525C9.17966 10.2435 9.16519 10.231 9.14735 10.2152C9.11168 10.1838 9.06237 10.1392 9.0036 10.0832C8.88663 9.97164 8.72903 9.81162 8.56631 9.61712C8.25927 9.25011 7.85576 8.66918 7.7761 8.00712L7.77589 8.00608C7.67765 7.17726 8.14459 6.16892 9.21256 6.04067L9.21443 6.04046ZM9.42151 9.16649L9.4211 9.1667C9.4211 9.1667 9.42146 9.16665 9.42193 9.16649H9.42151ZM9.36422 7.3115C9.25943 7.32433 9.18451 7.37501 9.12839 7.46087C9.06603 7.5563 9.02855 7.69864 9.04714 7.85546C9.08123 8.13525 9.28064 8.47633 9.54797 8.79587C9.62169 8.88398 9.69501 8.96332 9.76131 9.0317C9.84993 8.99235 9.94957 8.94544 10.0528 8.89024C10.4243 8.69152 10.7341 8.45032 10.864 8.21087L10.8653 8.20837L10.8909 8.1565C10.9448 8.03456 10.9564 7.91078 10.9369 7.81296C10.9169 7.71269 10.8662 7.63767 10.774 7.58629C10.6678 7.52948 10.5409 7.52658 10.4328 7.57837C10.1776 7.70055 9.87246 7.64075 9.68235 7.43129C9.60182 7.34256 9.48342 7.29784 9.36422 7.3115Z" | ||
| fill="var(--refly-text-1)" | ||
| /> | ||
| </svg> | ||
| | ||
| <span className="text-sm text-refly-text-0 leading-[1.43]"> | ||
| {t('workflowApp.publishToCommunity.label')} | ||
| </span> | ||
| <Tooltip title={t('workflowApp.publishToCommunity.help')} arrow={false}> | ||
| <Question | ||
| size={14} | ||
| color="var(--refly-question-icon-color, #888D92)" | ||
| className="cursor-pointer hover:opacity-80 transition-opacity" | ||
| /> | ||
| </Tooltip> | ||
| </div> | ||
| <Form.Item name="publishToCommunity" valuePropName="checked" className="mb-0"> | ||
| <Switch size="small" /> | ||
| </Form.Item> | ||
| </div> | ||
| </div> | ||
| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the Tooltip trigger keyboard‑accessible
Current trigger is an SVG icon, not focusable. Wrap it in a button with aria‑label so keyboard users can focus and get the tooltip.
Apply:
- <Tooltip title={t('workflowApp.publishToCommunity.help')} arrow={false}> - <Question - size={14} - color="var(--refly-question-icon-color, #888D92)" - className="cursor-pointer hover:opacity-80 transition-opacity" - /> - </Tooltip> + <Tooltip title={t('workflowApp.publishToCommunity.help')} arrow={false}> + <button + type="button" + aria-label={t('workflowApp.publishToCommunity.help')} + className="cursor-pointer hover:opacity-80 transition-opacity" + > + <Question + size={14} + color="var(--refly-question-icon-color, #888D92)" + aria-hidden="true" + /> + </button> + </Tooltip>As per coding guidelines
🤖 Prompt for AI Agents
In packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx around lines 505 to 542, the Tooltip trigger is an SVG/Question icon that isn't keyboard-focusable; wrap the icon in a semantic focusable element (e.g., a <button> or <span role="button" tabIndex={0}>) and give it an accessible label (aria-label) so keyboard users can focus it and receive the tooltip; ensure the wrapper preserves styling and pointer behavior, forwards click/keyboard events to open the Tooltip, and that the Tooltip component still receives the trigger element as its child. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx (1)
535-541: Keyboard accessibility issue already flagged.A past review comment already identified that the Tooltip trigger (Question icon) is not keyboard-accessible. Please address the previous feedback to wrap the icon in a focusable element.
🧹 Nitpick comments (5)
apps/api/src/modules/workflow-app/workflow-app.dto.ts (1)
35-35: Consider improving type safety for new fieldThe
publishToCommunityfield uses(app as any)to bypass TypeScript's type checking. While this is consistent with the existing pattern (e.g., line 31 forcoverStorageKey), it defeats type safety.Consider updating the
WorkflowAppPOtype definition to include thepublishToCommunityfield, which would eliminate the need for the type assertion and provide better compile-time safety.As per coding guidelines.
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
60-60: Consider improving type safety for request body parameterThe extraction of
publishToCommunityuses(body as any)to bypass TypeScript's type checking. SinceCreateWorkflowAppRequest(imported from@refly/openapi-schemaat line 3) should already include the optionalpublishToCommunityfield based on the generated types, this type assertion may be unnecessary.Consider verifying that the
bodyparameter is properly typed asCreateWorkflowAppRequestand accessing the field directly without the cast. This would provide better type safety and IDE autocomplete support.Note: This pattern is used consistently throughout the function (lines 58-61), so a broader refactor of the function signature might be beneficial.
As per coding guidelines.
packages/openapi-schema/src/schemas.gen.ts (1)
9394-9397: Add default value to publishToCommunity for clarity; write-only design confirmed.The field is intentionally write-only (present only in CreateWorkflowAppRequest, not in WorkflowApp response), with shareId as the canonical published-state indicator. However, add a default value to avoid ambiguous client semantics (undefined vs. false).
Apply to the OpenAPI spec (then regenerate):
publishToCommunity: type: boolean description: Whether to publish this app to the community + default: falseOptionally, add an explicit
writeOnly: trueflag to the spec for clarity, preventing future confusion about whether this field should appear in the response.packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx (2)
515-530: Consider extracting the inline SVG icon.The 16-line inline SVG increases code verbosity. Extract it to a separate icon component or add it to the refly-icons package for better maintainability and reusability.
For example, create a
PublishToCommunityIconcomponent:// In a separate file or icon collection const PublishToCommunityIcon = ({ size = 16 }: { size?: number }) => ( <svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16" fill="none" > {/* SVG paths here */} </svg> );Then use it:
<PublishToCommunityIcon /> <span className="text-sm text-refly-text-0 leading-[1.43]"> {t('workflowApp.publishToCommunity.label')} </span>
543-545: Add aria-label to Switch for better screen reader support.While the Form.Item provides some context, adding an explicit aria-label to the Switch improves accessibility for screen reader users.
Apply this diff:
- <Form.Item name="publishToCommunity" valuePropName="checked" className="mb-0"> - <Switch size="small" /> + <Form.Item name="publishToCommunity" valuePropName="checked" className="mb-0"> + <Switch + size="small" + aria-label={t('workflowApp.publishToCommunity.label')} + /> </Form.Item>As per coding guidelines
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/api/prisma/schema.prisma(1 hunks)apps/api/src/modules/workflow-app/workflow-app.dto.ts(1 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(3 hunks)packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx(7 hunks)packages/ai-workspace-common/src/requests/types.gen.ts(3 hunks)packages/i18n/src/en-US/ui.ts(1 hunks)packages/i18n/src/zh-Hans/ui.ts(1 hunks)packages/openapi-schema/schema.yml(1 hunks)packages/openapi-schema/src/schemas.gen.ts(1 hunks)packages/openapi-schema/src/types.gen.ts(1 hunks)packages/request/src/requests/types.gen.ts(3 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/api/prisma/schema.prisma
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/openapi-schema/schema.yml
- packages/i18n/src/en-US/ui.ts
- packages/i18n/src/zh-Hans/ui.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Always use optional chaining (?.) when accessing object properties.
Always use nullish coalescing (??) or default values for potentially undefined values.
Always check array existence before using array methods.
Always validate object properties before destructuring.
Always use single quotes for string literals in JavaScript/TypeScript code.Use JSDoc style comments for functions and classes in JavaScript/TypeScript
**/*.{js,jsx,ts,tsx}: Use single quotes for string literals
Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Validate object properties before destructuring
Use ES6+ features like arrow functions, destructuring, and spread operators
Use async/await instead of raw promises for asynchronous code
**/*.{js,jsx,ts,tsx}: Maximum line length of 100 characters
Use 2 spaces for indentation, no tabs
Use semicolons at the end of statements
Include spaces around operators (e.g.,a + binstead ofa+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement
No trailing whitespace at the end of lines
**/*.{js,jsx,ts,tsx}: Each file should contain only one main component
File names should match the component name
Keep component files focused with minimal dependencies
Group import statements in the following order: 1. React/framework libraries 2. Third-party libraries 3. Internal modules 4. Relative path imports (parent directories first, then child directories) 5. Type imports 6. Style imports
Sort imports alphabetically within each group
Leave a blank line between import groups
Components should be organized in the following order: 1. Import statements 2. Type definitions 3. Constant declarations 4. Component function 5. Hook calls 6. Event handlers 7. Helper render functions 8. JSX return statement
Extract complex logic in...
Files:
packages/openapi-schema/src/types.gen.tspackages/request/src/requests/types.gen.tspackages/openapi-schema/src/schemas.gen.tsapps/api/src/modules/workflow-app/workflow-app.service.tspackages/ai-workspace-common/src/requests/types.gen.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsxapps/api/src/modules/workflow-app/workflow-app.dto.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)
**/*.{ts,tsx}: Avoid usinganytype whenever possible - it defeats the purpose of TypeScript
When tempted to useany, useunknowntype instead and add proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new ones
Use TypeScript's utility types to derive new types:Partial<T>,Pick<T, K>,Omit<T, K>,Readonly<T>,Record<K, T>
Use union types and intersection types to combine existing types
Always import types explicitly using theimport typesyntax
Group type imports separately from value imports
Minimize creating local type aliases for imported typesExplicitly type props interfaces/types
Files:
packages/openapi-schema/src/types.gen.tspackages/request/src/requests/types.gen.tspackages/openapi-schema/src/schemas.gen.tsapps/api/src/modules/workflow-app/workflow-app.service.tspackages/ai-workspace-common/src/requests/types.gen.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsxapps/api/src/modules/workflow-app/workflow-app.dto.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)
Follow NestJS patterns for backend code
Files:
packages/openapi-schema/src/types.gen.tspackages/request/src/requests/types.gen.tspackages/openapi-schema/src/schemas.gen.tsapps/api/src/modules/workflow-app/workflow-app.service.tspackages/ai-workspace-common/src/requests/types.gen.tsapps/api/src/modules/workflow-app/workflow-app.dto.ts
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)
**/*.{css,scss,sass,less,styl,js,jsx,ts,tsx}: Use Primary color (#155EEF) as the main brand color for buttons, links, and accents
Use Error color (#F04438) for error states and destructive actions
Use Success color (#12B76A) for success states and confirmations
Use Warning color (#F79009) for warnings and important notifications
Use Info color (#0BA5EC) for informational elements
Files:
packages/openapi-schema/src/types.gen.tspackages/request/src/requests/types.gen.tspackages/openapi-schema/src/schemas.gen.tsapps/api/src/modules/workflow-app/workflow-app.service.tspackages/ai-workspace-common/src/requests/types.gen.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsxapps/api/src/modules/workflow-app/workflow-app.dto.ts
**/*.{css,scss,less,styl,js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)
**/*.{css,scss,less,styl,js,jsx,ts,tsx}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Maintain a minimum contrast ratio of 4.5:1 for text
Files:
packages/openapi-schema/src/types.gen.tspackages/request/src/requests/types.gen.tspackages/openapi-schema/src/schemas.gen.tsapps/api/src/modules/workflow-app/workflow-app.service.tspackages/ai-workspace-common/src/requests/types.gen.tspackages/ai-workspace-common/src/components/workflow-app/create-modal.tsxapps/api/src/modules/workflow-app/workflow-app.dto.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{jsx,tsx}: Always use tailwind css to style the component.
Always wrap pure components with React.memo to prevent unnecessary re-renders.
Always use useMemo for expensive computations or complex object creation.
Always use useCallback for function props to maintain referential equality.
Always specify proper dependency arrays in useEffect to prevent infinite loops.
Always avoid inline object/array creation in render to prevent unnecessary re-renders.
Always use proper key props when rendering lists.
Always split nested components with closures into separate components to avoid performance issues and improve code maintainability.
**/*.{jsx,tsx}: Always wrap pure components with React.memo to prevent unnecessary re-renders
Always use useMemo for expensive computations or complex object creation
Always use useCallback for function props to maintain referential equality
Always specify proper dependency arrays in useEffect to prevent infinite loops
Always avoid inline object/array creation in render to prevent unnecessary re-renders
Always use proper key props when rendering lists (avoid using index when possible)
Always split nested components with closures into separate components
Use lazy loading for components that are not immediately needed
Debounce handlers for events that might fire rapidly (resize, scroll, input)
Implement fallback UI for components that might fail
Use error boundaries to catch and handle runtime errors
Use Tailwind CSS for styling components
Group related utility classes together
Prefer Tailwind utilities over custom CSS when possible
**/*.{jsx,tsx}: Place each attribute on a new line when a component has multiple attributes in JSX
Use self-closing tags for elements without children in JSX
Keep JSX expressions simple, extract complex logic to variables
Put closing brackets for multi-line JSX on a new lineUse React best practices for frontend code
Files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)
**/*.tsx: Use the translation wrapper component when displaying user-facing text in components
Ensure all user-facing text in components is translatable
Files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/02-project-structure.mdc:0-0 Timestamp: 2025-07-21T01:56:03.072Z Learning: Shared UI components and utilities are located in packages/ai-workspace-common 📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Ensure all interactive elements are keyboard accessible Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Form*.{js,jsx,ts,tsx} : Ensure proper labeling and accessibility Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Include appropriate ARIA attributes for complex components Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Form*.{js,jsx,ts,tsx,css,scss,less,styl} : Use clear visual indicators for focus, hover, and error states Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Support screen readers with semantic HTML elements Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Provide alternative text for images and icons Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Button*.{js,jsx,ts,tsx,css,scss,less,styl} : Use consistent padding, border radius, and hover states for all buttons Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-10T02:52:10.899Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/08-contributing-guidelines.mdc:0-0 Timestamp: 2025-07-10T02:52:10.899Z Learning: Applies to **/*.{jsx,tsx} : Use React best practices for frontend code Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-10T02:52:43.495Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/10-testing-guidelines.mdc:0-0 Timestamp: 2025-07-10T02:52:43.495Z Learning: Applies to **/*.{test,spec}.{jsx,tsx} : Use test utilities from React Testing Library Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Extract complex logic into custom hooks Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Always use useCallback for function props to maintain referential equality Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:21.585Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursorrules:0-0 Timestamp: 2025-07-21T01:55:21.585Z Learning: Applies to **/*.{jsx,tsx} : Always use useCallback for function props to maintain referential equality. Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Group related utility classes together Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Use React state hooks for component internal state Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Button*.{js,jsx,ts,tsx} : Include appropriate loading states for async actions Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:55:52.528Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/01-code-style.mdc:0-0 Timestamp: 2025-07-21T01:55:52.528Z Learning: Applies to **/*.{jsx,tsx} : Implement fallback UI for components that might fail Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T01:56:57.973Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/05-code-organization.mdc:0-0 Timestamp: 2025-07-21T01:56:57.973Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Extract repeated JSX patterns into reusable components Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
📚 Learning: 2025-07-21T06:47:12.834Z
Learnt from: CR Repo: refly-ai/refly PR: 0 File: .cursor/rules/11-ui-design-patterns.mdc:0-0 Timestamp: 2025-07-21T06:47:12.834Z Learning: Applies to **/*Form*.{js,jsx,ts,tsx,css,scss,less,styl} : Group related form elements with appropriate spacing Applied to files:
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx
🧬 Code graph analysis (1)
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx (3)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
createWorkflowApp(56-205)apps/api/src/modules/workflow-app/workflow-app.controller.ts (1)
createWorkflowApp(35-41)packages/ai-workspace-common/src/requests/services.gen.ts (1)
createWorkflowApp(2250-2261)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (5)
packages/request/src/requests/types.gen.ts (3)
4383-4389: LGTM: Well-documented storage key refactor for voice audio.The change from
audioEntityIdtostorageKeyis a cleaner approach for direct file references. The priority documentation ("takes priority over audioUrl") is clear and prevents ambiguity. The format specification"static/{uuid}"provides helpful guidance for API consumers.
4412-4418: LGTM: Consistent storage key implementation for background.The background configuration follows the same pattern as the voice section, ensuring consistency across the API. The priority model and format specification are identical, making the API predictable and easy to use.
6645-6648: LGTM: Clear addition for community publishing feature.The
publishToCommunityfield is well-documented and appropriately typed as an optional boolean. This aligns with the PR's broader community publishing implementation mentioned in the AI summary.packages/openapi-schema/src/types.gen.ts (1)
6645-6648: LGTM - Auto-generated type definitionThe new optional
publishToCommunityfield is properly typed and documented. Since this is an auto-generated file, ensure the source OpenAPI schema (likelypackages/openapi-schema/schema.yml) is also correct and properly maintained.apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
141-141: LGTM - Proper field persistenceThe
publishToCommunityfield is correctly persisted in both the update operation (line 141) and create operation (line 160), ensuring the feature flag is stored consistently regardless of whether the workflow app is being created for the first time or updated.Also applies to: 160-160
| publishToCommunity: publishToCommunity ?? false, | ||
| resultNodeIds: selectedResults, | ||
| }, | ||
| } as any, // TODO: Remove type assertion after running pnpm codegen in packages/openapi-schema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify codegen has been executed.
The TODO comment indicates this type assertion should be removed after running codegen in packages/openapi-schema. Confirm the codegen has been executed to ensure type safety.
🏁 Script executed:
#!/bin/bash # Check if the CreateWorkflowAppRequest type includes publishToCommunity in the generated schema rg -A 5 "export type CreateWorkflowAppRequest" packages/openapi-schema/src/types.gen.tsLength of output: 93
--- 🏁 Script executed: ```shell #!/bin/bash # Get the complete CreateWorkflowAppRequest type definition rg -A 50 "export type CreateWorkflowAppRequest = " packages/openapi-schema/src/types.gen.ts | head -60 Length of output: 822
Remove the unnecessary as any type assertion now that codegen has been executed.
Codegen has been successfully run—the generated CreateWorkflowAppRequest type now includes publishToCommunity?: boolean;. The type assertion as any on line 322 is no longer needed and should be removed to restore type safety.
🤖 Prompt for AI Agents
packages/ai-workspace-common/src/components/workflow-app/create-modal.tsx around line 322: the object is currently cast with "as any" which is no longer needed because codegen added publishToCommunity?: boolean to CreateWorkflowAppRequest; remove the "as any" type assertion on that line so the value is typed as CreateWorkflowAppRequest, then run TypeScript typecheck (pnpm build/typecheck) and adjust any property names or optional flags if the compiler surfaces mismatches. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/api/src/modules/workflow-app/email-templates.ts (1)
19-29: Consider making the Discord link configurable.The Discord invite link is hardcoded in the template. While this works, consider making it configurable via
ConfigServicefor easier updates and environment-specific variations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/api/src/modules/workflow-app/email-templates.ts(1 hunks)apps/api/src/modules/workflow-app/workflow-app.module.ts(2 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T02:51:12.185Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.185Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field has a `unique` constraint, making `findUnique` the correct and preferred Prisma method when querying by appId alone. Applied to files:
apps/api/src/modules/workflow-app/workflow-app.service.ts
📚 Learning: 2025-09-10T02:51:12.184Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.184Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field is a unique selector, making `findUnique` the correct Prisma method to use when querying by appId. Applied to files:
apps/api/src/modules/workflow-app/workflow-app.service.ts
🧬 Code graph analysis (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
packages/request/src/requests/types.gen.ts (2)
User(474-483)CreateWorkflowAppRequest(6612-6649)apps/api/src/modules/workflow-app/email-templates.ts (2)
generateWorkflowAppReviewEmailHTML(37-90)WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE(19-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (6)
apps/api/src/modules/workflow-app/workflow-app.module.ts (1)
12-12: LGTM!The NotificationModule import and module registration correctly wires the notification service for the workflow app's email notification feature.
Also applies to: 23-23
apps/api/src/modules/workflow-app/workflow-app.service.ts (5)
25-30: LGTM!The imports are correctly added to support the email notification feature for community-published workflow apps.
60-61: LGTM!The service injections are properly added to support email notifications and configuration retrieval.
177-202: LGTM!The share creation flow correctly captures the
shareIdand updates the WorkflowApp record. Error handling is appropriate, ensuring that share creation failures don't block the workflow app creation.
68-68: No issues found - enum values are correct.The
publishReviewStatusenum includes both'reviewing'and'init'values used in the code. The implementation correctly mapspublishToCommunityto the appropriate enum value.
204-229: Email notification and URL routing are correctly implemented.Verification confirms both aspects are sound:
Route pattern verified: The frontend defines
path: '/app/:shareId'atapps/web/src/routes/index.tsx:89, which matches the constructed template link in line 208.Origin configuration is intentional: The comma-separated
originconfig supports multiple allowed origins. In main.ts, the full list is passed to CORS enablement, while in workflow-app.service.ts, the first origin is selected for public-facing email links—a reasonable pattern for multi-environment deployments.The email notification flow is correctly implemented with proper guards, error handling, and URL construction.
| export function generateWorkflowAppReviewEmailHTML( | ||
| templateName: string, | ||
| templateLink: string, | ||
| ): string { | ||
| const template = WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE; | ||
| const mainMessage = template.body.mainMessage.replace('{{template_name}}', templateName); | ||
| | ||
| return ` | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| <title>${template.subject}</title> | ||
| </head> | ||
| <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5; line-height: 1.6; color: #1c1f23;"> | ||
| <table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f5f5f5; padding: 20px 0;"> | ||
| <tr> | ||
| <td align="center" style="padding: 20px 0;"> | ||
| <table role="presentation" style="width: 100%; max-width: 600px; background-color: #ffffff; border-radius: 8px; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> | ||
| <tr> | ||
| <td style="padding: 32px 32px 24px 32px;"> | ||
| <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${template.body.greeting}</p> | ||
| <p style="margin: 0 0 24px 0; font-size: 16px; color: #1c1f23;">${mainMessage}</p> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <td style="padding: 0 32px 16px 32px;"> | ||
| <p style="margin: 0 0 8px 0; font-size: 14px; color: #1c1f23;"> | ||
| <strong style="color: #1c1f23;">${template.body.templateLinkLabel}:</strong> | ||
| </p> | ||
| <p style="margin: 0; font-size: 14px;"> | ||
| <a href="${templateLink}" style="color: #155EEF; text-decoration: none; word-break: break-all;">${templateLink}</a> | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <td style="padding: 0 32px 24px 32px;"> | ||
| <p style="margin: 0 0 8px 0; font-size: 14px; color: #1c1f23;">${template.body.discordMessage}</p> | ||
| <p style="margin: 0; font-size: 14px;"> | ||
| <strong style="color: #1c1f23;">Discord:</strong> | ||
| <a href="${template.body.discordLink}" style="color: #155EEF; text-decoration: none; margin-left: 8px;">${template.body.discordLink}</a> | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| </table> | ||
| </td> | ||
| </tr> | ||
| </table> | ||
| </body> | ||
| </html> | ||
| `; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix XSS vulnerability: escape user-controlled input.
The function inserts templateName (derived from user input canvasData.title) and templateLink directly into the HTML without escaping. This creates an XSS vulnerability if a malicious user provides a template name containing HTML or script tags.
Apply this diff to add HTML escaping:
+/** + * Escape HTML special characters to prevent XSS + */ +function escapeHtml(unsafe: string): string { + return unsafe + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + export function generateWorkflowAppReviewEmailHTML( templateName: string, templateLink: string, ): string { const template = WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE; - const mainMessage = template.body.mainMessage.replace('{{template_name}}', templateName); + const escapedTemplateName = escapeHtml(templateName); + const escapedTemplateLink = escapeHtml(templateLink); + const mainMessage = template.body.mainMessage.replace('{{template_name}}', escapedTemplateName); return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <title>${template.subject}</title> + <title>${escapeHtml(template.subject)}</title> </head> <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5; line-height: 1.6; color: #1c1f23;"> <table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f5f5f5; padding: 20px 0;"> <tr> <td align="center" style="padding: 20px 0;"> <table role="presentation" style="width: 100%; max-width: 600px; background-color: #ffffff; border-radius: 8px; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> <tr> <td style="padding: 32px 32px 24px 32px;"> - <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${template.body.greeting}</p> + <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${escapeHtml(template.body.greeting)}</p> <p style="margin: 0 0 24px 0; font-size: 16px; color: #1c1f23;">${mainMessage}</p> </td> </tr> <tr> <td style="padding: 0 32px 16px 32px;"> <p style="margin: 0 0 8px 0; font-size: 14px; color: #1c1f23;"> - <strong style="color: #1c1f23;">${template.body.templateLinkLabel}:</strong> + <strong style="color: #1c1f23;">${escapeHtml(template.body.templateLinkLabel)}:</strong> </p> <p style="margin: 0; font-size: 14px;"> - <a href="${templateLink}" style="color: #155EEF; text-decoration: none; word-break: break-all;">${templateLink}</a> + <a href="${escapedTemplateLink}" style="color: #155EEF; text-decoration: none; word-break: break-all;">${escapedTemplateLink}</a> </p> </td> </tr> <tr> <td style="padding: 0 32px 24px 32px;"> - <p style="margin: 0 0 8px 0; font-size: 14px; color: #1c1f23;">${template.body.discordMessage}</p> + <p style="margin: 0 0 8px 0; font-size: 14px; color: #1c1f23;">${escapeHtml(template.body.discordMessage)}</p> <p style="margin: 0; font-size: 14px;"> - <strong style="color: #1c1f23;">Discord:</strong> - <a href="${template.body.discordLink}" style="color: #155EEF; text-decoration: none; margin-left: 8px;">${template.body.discordLink}</a> + <strong style="color: #1c1f23;">Discord:</strong> + <a href="${escapeHtml(template.body.discordLink)}" style="color: #155EEF; text-decoration: none; margin-left: 8px;">${escapeHtml(template.body.discordLink)}</a> </p> </td> </tr> </table> </td> </tr> </table> </body> </html> `; }🤖 Prompt for AI Agents
In apps/api/src/modules/workflow-app/email-templates.ts around lines 37 to 90, the function inserts user-controlled templateName and templateLink directly into the HTML causing an XSS risk; fix by introducing an HTML-escaping helper (e.g., escapeHtml that replaces &, <, >, ", ' and /) and use it when interpolating templateName and the visible templateLink text, and for the href attribute either validate/normalize the URL or wrap it with encodeURI after validation before interpolating into href so no raw user input is injected into the HTML. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
137-154: Fix publishReviewStatus state preservation in the update path.The current implementation resets
publishReviewStatusto either'reviewing'or'init'on every update (line 150), which means previously approved or rejected apps lose their review status whenever the workflow app is modified. This breaks the state machine: an approved app becomes "reviewing" again after any update.Recommended fix: Preserve the existing
publishReviewStatusduring updates unlesspublishToCommunityis being explicitly changed. Only reset the status when transitioning fromfalsetotrue(set to'reviewing') or fromtruetofalse(set to'init').For example:
publishReviewStatus: publishToCommunity && !existingWorkflowApp.publishToCommunity ? 'reviewing' : publishToCommunity === false && existingWorkflowApp.publishToCommunity ? 'init' : existingWorkflowApp.publishReviewStatusNote: The create path (line 170) is correct as-is since new apps should start with fresh status.
🧹 Nitpick comments (2)
apps/api/prisma/schema.prisma (1)
609-631: Solid many-to-many join table design.The
CanvasTemplateCategoryRelationmodel properly implements the join table with appropriate constraints and indexes. The composite unique constraint on (templateId, categoryId) prevents duplicate associations, and the indexed deletedAt fields support efficient soft-delete queries.One optional note: consider adding explicit
@relationnames on lines 623 and 625 for extra clarity in complex migrations, though implicit naming should work fine here given the single FK context.apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
66-68: Remove unnecessary type assertions.The properties
coverStorageKey,remixEnabled, andpublishToCommunityare all defined in theCreateWorkflowAppRequesttype, so the(body as any)type assertions are unnecessary and bypass TypeScript's type safety.Apply this diff to access properties with proper typing:
- const coverStorageKey = (body as any).coverStorageKey; - const remixEnabled = (body as any).remixEnabled ?? false; - const publishToCommunity = (body as any).publishToCommunity ?? false; + const coverStorageKey = body.coverStorageKey; + const remixEnabled = body.remixEnabled ?? false; + const publishToCommunity = body.publishToCommunity ?? false;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/api/prisma/schema.prisma(4 hunks)apps/api/src/modules/workflow-app/email-templates.ts(1 hunks)apps/api/src/modules/workflow-app/workflow-app.module.ts(2 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/api/src/modules/workflow-app/workflow-app.module.ts
- apps/api/src/modules/workflow-app/email-templates.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T02:51:12.185Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.185Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field has a `unique` constraint, making `findUnique` the correct and preferred Prisma method when querying by appId alone. Applied to files:
apps/api/prisma/schema.prismaapps/api/src/modules/workflow-app/workflow-app.service.ts
📚 Learning: 2025-09-10T02:51:12.184Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.184Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field is a unique selector, making `findUnique` the correct Prisma method to use when querying by appId. Applied to files:
apps/api/prisma/schema.prismaapps/api/src/modules/workflow-app/workflow-app.service.ts
🧬 Code graph analysis (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
packages/ai-workspace-common/src/requests/types.gen.ts (2)
User(474-483)CreateWorkflowAppRequest(6612-6649)apps/api/src/modules/workflow-app/email-templates.ts (2)
generateWorkflowAppReviewEmailHTML(35-76)WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE(18-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (9)
apps/api/prisma/schema.prisma (4)
399-404: LGTM!The
PublishReviewStatusenum is well-defined with appropriate values for the publish-to-community review workflow.
539-577: Excellent backward-compatible migration pattern.The deprecation strategy for
CanvasTemplateis well-executed: the oldcategoryIdandcategoryfields are preserved with clear deprecation notes, while the newcategoriesmany-to-many relation follows a different field name, avoiding conflicts. This allows incremental migration of consuming code.
584-607: LGTM!The mirror deprecation pattern on
CanvasTemplateCategorymaintains consistency withCanvasTemplate, clearly guiding consumers toward the new many-to-many relation while preserving the old one-to-many for compatibility.
864-871: LGTM!The three new fields in
WorkflowAppfollow established patterns:
publishToCommunitydefaults to false (safe opt-in)publishReviewStatusreferences the new enum with default init stateremarksis nullable for optional admin feedback- All field mappings and types are consistent with existing schema conventions
apps/api/src/modules/workflow-app/workflow-app.service.ts (5)
25-30: LGTM!The new imports for NotificationService, ConfigService, and email template utilities are properly structured and align with the feature implementation.
60-61: LGTM!Dependency injection follows NestJS best practices with proper readonly modifiers.
177-202: LGTM!The shareId handling is implemented correctly with proper error handling that logs failures without breaking the main workflow app creation flow.
212-218: No issues found with email handling.The
NotificationService.sendEmailmethod at lines 128-189 ofapps/api/src/modules/notification/notification.service.tsproperly handles undefined or emptyuser.emailthrough:
- Optional chaining (
user?.email) preventing errors when user or email is undefined- Multiple fallback strategies: direct email fallback (line 149), then database lookup (lines 153-161)
- Final validation throwing
ParamsErrorif no valid receiver is found (lines 163-165)The code robustly handles all edge cases and safely prevents sending emails without a valid recipient.
207-208: The origin configuration always has a default value—no fix is required.The configuration defined in
apps/api/src/modules/config/app.config.tsline 6 ensures origin is always set:origin: process.env.ORIGIN || 'http://localhost:5700'. This means the fallback to an empty string in workflow-app.service.ts line 207 is unreachable, andtemplateLinkwill always be an absolute URL, never a relative one. The code is already safe.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/modules/share/share-creation.service.ts (1)
1030-1067: Remove the redundant minimap re-publishing increateOrUpdateWorkflowAppShareThe code at lines 1048–1051 attempts to re-publish
canvasDataWithId.minimapUrl, but this is inconsistent with how minimaps are handled:
- In
createShareForCanvas(lines 312–315), the minimap is published fromcanvas.minimapStorageKeybefore calling the helper, socanvasData.minimapUrlalready contains a URL.- In
createShareForWorkflowApp, no minimap publishing occurs before the helper is called, socanvasData.minimapUrlis undefined (sincegetCanvasRawData()andprocessCanvasForShare()do not populate it).This means lines 1048–1051 either function as dead code (passing undefined to
publishFile, which returns an empty string) or represent misplaced logic. Additionally, ifminimapUrlwere already a URL, passing it topublishFile()(which expects a storage key) would fail.Solution: Remove lines 1048–1051. If workflow app minimaps need publishing, fetch and publish
canvas.minimapStorageKeybeforehand (as done in canvas shares), or skip this step if minimaps are not required for workflow app shares.
♻️ Duplicate comments (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
68-69: Email review notifications currently fire on every save whilepublishToCommunityis true
createWorkflowAppsends the review email whenever it runs withpublishToCommunitytruthy and ashareId(Lines 213–238), regardless of whether this is an initial publish or an edit to an already published app. Because the function also handles updates (Lines 137–155), this will resend review emails on every update wherepublishToCommunitystays true, which matches the earlier concern in the past review comments.To avoid duplicate notifications and only email on first-time submission for review, consider tracking an "initial publish" condition based on the previous DB state:
@@ - const existingWorkflowApp = await this.prisma.workflowApp.findFirst({ - where: { canvasId, uid: user.uid, deletedAt: null }, - }); + const existingWorkflowApp = await this.prisma.workflowApp.findFirst({ + where: { canvasId, uid: user.uid, deletedAt: null }, + }); + const wasPublishedToCommunity = existingWorkflowApp?.publishToCommunity ?? false; + const isInitialPublish = publishToCommunity && !wasPublishedToCommunity; @@ - // Send email notification if template is submitted for review - if (publishToCommunity && shareId) { + // Send email notification only on initial submit for review + if (isInitialPublish && shareId) {If your review pipeline cares about
publishReviewStatustransitions instead, you could instead send when the status changes from'init'to'reviewing'.Please confirm which behavior is intended; as written, users will get multiple review emails for the same app if they keep it published while editing.
Also applies to: 149-151, 169-170, 213-238
🧹 Nitpick comments (4)
apps/api/prisma/schema.prisma (1)
858-872: Consider database constraints and indexes for publish workflow fields.The new publishing fields (
publishToCommunity,publishReviewStatus,remarks,templateContent) are well-structured, but two refinements may improve queryability and safety:
- remarks field: Consider adding
@db.Textor size constraint. This field will store admin review notes and should have explicit sizing to prevent unexpected bloat.- Missing indexes: Consider adding an index on
publishReviewStatusand a composite index on(uid, publishReviewStatus)to optimize queries filtering for pending/approved apps.Proposed index additions:
@@index([publishReviewStatus]) @@index([uid, publishReviewStatus])Optional: Add field-level constraint to remarks:
remarks String? @db.VarChar(1000) @map("remarks")apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
68-69: RecomputingpublishReviewStatuson every save may override manual review statesOn both update and create (Lines 149–151, 169–170),
publishReviewStatusis recomputed purely frompublishToCommunity:publishReviewStatus: publishToCommunity ? 'reviewing' : 'init',This means any downstream transition (e.g.
'approved','rejected') will be reset to'reviewing'on every subsequent edit wherepublishToCommunityremains true.If the intended behavior is:
- Set
'reviewing'only on first-time submission; and- Preserve whatever status the reviewer later sets,
you could instead only change
publishReviewStatuswhen transitioning from "not published" to "published", e.g.:const nextReviewStatus = publishToCommunity && !wasPublishedToCommunity ? 'reviewing' : existingWorkflowApp?.publishReviewStatus ?? 'init';and use
nextReviewStatusin the update branch, while keeping the create branch as is.Please confirm whether automatic resets to
'reviewing'on every edit are desired.Also applies to: 149-151, 169-170
176-201: Share and templateShare wiring looks coherentThe new flow that:
- Calls
createShareForWorkflowAppand receives{ shareRecord, templateShareRecord }, and- Persists both
shareIdandtemplateShareIdback onto theworkflowApprecord (Lines 176–201),is consistent with the new sharing model and keeps the main DB row as the source of truth for both links. The error handling (logging without throwing) preserves the previous behavior where workflow app creation isn’t blocked by share failures.
One follow-up to consider (not blocking): unpublishing from community (
publishToCommunitytoggled false) now clearstemplateShareIdon the workflow app but leaves the existing template share record inshareRecorduntouched. That’s acceptable functionally but can accumulate unused template share rows over time; if that’s a concern, you could soft-delete or reuse the same template share on re-publish.apps/api/src/modules/share/share-creation.service.ts (1)
1035-1036: Guard againstcreditUsagebeing undefined to avoid NaN in public data
creditUsageis optional onCreateShareRequest, but in the workflow-app path it’s multiplied directly:creditUsage: Math.ceil(creditUsage * this.configService.get('credit.executionCreditMarkup')),(Lines 1035–1036, 1067) and the value is passed in from
createShareForWorkflowAppviaparam.creditUsage(Lines 1118–1120, 1158, 1194).If
creditUsageis ever omitted on aworkflowAppshare request (e.g., some call sites don’t populate it), this will produceNaNin the shared JSON. That’s not catastrophic but will surface as confusing metadata.Suggestion:
const markup = Number(this.configService.get('credit.executionCreditMarkup') ?? 1); const normalizedCreditUsage = creditUsage ?? 0; const publicData = { // ... creditUsage: Math.ceil(normalizedCreditUsage * markup), // ... };This keeps the field well-defined even when
creditUsageis absent or the config value is missing.Also applies to: 1067-1067, 1118-1120, 1158-1158, 1189-1195
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/api/prisma/schema.prisma(4 hunks)apps/api/src/modules/share/share-creation.service.ts(5 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T02:51:12.185Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.185Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field has a `unique` constraint, making `findUnique` the correct and preferred Prisma method when querying by appId alone. Applied to files:
apps/api/prisma/schema.prisma
📚 Learning: 2025-09-10T02:51:12.184Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.184Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field is a unique selector, making `findUnique` the correct Prisma method to use when querying by appId. Applied to files:
apps/api/prisma/schema.prisma
🧬 Code graph analysis (2)
apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
packages/ai-workspace-common/src/requests/types.gen.ts (2)
User(474-483)CreateWorkflowAppRequest(6612-6649)apps/api/src/modules/workflow-app/email-templates.ts (2)
generateWorkflowAppReviewEmailHTML(35-76)WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE(18-27)
apps/api/src/modules/share/share-creation.service.ts (2)
packages/openapi-schema/src/types.gen.ts (5)
User(474-483)WorkflowApp(6658-6711)SharedCanvasData(2520-2525)ShareRecord(3301-3338)CreateShareRequest(3409-3446)packages/errors/src/errors.ts (1)
ShareNotFoundError(203-209)
🔇 Additional comments (7)
apps/api/prisma/schema.prisma (5)
399-404: PublishReviewStatus enum is well-structured.The enum properly models the community publishing review workflow with appropriate state transitions (init → reviewing → approved/rejected).
539-582: Verify dual-relation migration strategy in service layer.The model maintains both the old
categoryId/categoryrelation and the newcategoriesmany-to-many relation for backward compatibility. Ensure the service layer enforces that:
- Only one path (old or new) is used for writes to prevent data inconsistency
- Read operations consider both relations during the transition period
- A deprecation timeline and migration plan exists for removing
categoryIdand thecategoryrelation
609-631: CanvasTemplateCategoryRelation model is well-designed.The many-to-many junction table follows best practices with proper unique constraints, soft-delete support, and appropriate indexes for common lookup patterns.
584-607: Verify dual-relation migration strategy (consistent with CanvasTemplate changes).Similar to the changes in
CanvasTemplate, both old and new relations coexist here. Ensure service layer enforces single-path writes and confirm a deprecation timeline for removingtemplatesandcategoryfields from queries.
837-885: Verify migration and backfill strategy for new WorkflowApp fields.New fields (
publishToCommunity,publishReviewStatus,remarks,templateShareId,templateContent) have appropriate defaults, but confirm:
- Existing
WorkflowApprecords will be backfilled withpublishToCommunity = falseandpublishReviewStatus = init- Database migration includes proper rollback/rollforward strategy
- Service layer safely handles optional
templateShareIdandremarksfieldsapps/api/src/modules/share/share-creation.service.ts (2)
5-5: Using generatedShareRecord/WorkflowApptypes is appropriateImporting
ShareRecordandWorkflowAppfrom the generated Prisma client (Line 5) keeps this service aligned with the DB schema and avoids custom type drift. No issues here.
1118-1203: Workflow app share/template-share flow is well-factored and idempotentThe new
createShareForWorkflowAppimplementation (Lines 1118–1203) is a nice consolidation:
- It reuses
processCanvasForShareso workflow apps benefit from the same resource/document/skill/codeArtifact sharing pipeline as canvases.- Regular share creation is idempotent via
existingShareRecordby entityId/entityType/uid.- Template share creation is gated on
workflowApp.publishToCommunityand usestemplateShareIdto either create or update a distinct share, keeping community templates independent from regular shares.- Returning
{ shareRecord, workflowApp, templateShareRecord }integrates cleanly withWorkflowAppServiceand withprocessCreateShareJobwhere the return value is ignored.Overall this structure looks solid and matches the rest of the sharing service patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
213-242: Duplicate emails sent on every update whenpublishToCommunityremains true.The email notification block executes after both the update path (lines 137-154) and create path (lines 155-174). Since the condition only checks
if (publishToCommunity && shareId)without distinguishing between new apps and updates, users will receive a review notification email every time they update their workflow app whilepublishToCommunityis true.To fix this, send emails only on initial publish. Consider one of these approaches:
Option 1: Send email only for new apps
// Send email notification if template is submitted for review - if (publishToCommunity && shareId) { + if (!existingWorkflowApp && publishToCommunity && shareId) { try {Option 2: Send email only when status transitions to 'reviewing'
This requires checking if the previous
publishReviewStatuswas 'init' and the new status is 'reviewing', which would need additional logic to track the previous state before the update.apps/api/src/modules/workflow-app/email-templates.ts (1)
45-98: Critical XSS vulnerability: user-controlled input injected into HTML without escaping.The function directly interpolates
templateName(lines 51, 53, 68, 78),templateLink(line 82), andnote(line 84) into HTML without escaping. SincetemplateNameis derived from user-controlledcanvasData.title, a malicious user could inject HTML or JavaScript (e.g.,<script>alert('XSS')</script>or<img src=x onerror=alert(1)>), leading to XSS attacks when the email is rendered.Add HTML escaping before interpolation:
+/** + * Escape HTML special characters to prevent XSS + */ +function escapeHtml(unsafe: string): string { + return unsafe + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + export function generateWorkflowAppReviewEmailHTML( templateName: string, templateLink: string, note?: string, ): string { const template = WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE; - const subject = template.subject.replace('{{template_name}}', templateName); + const escapedTemplateName = escapeHtml(templateName); + const escapedTemplateLink = escapeHtml(templateLink); + const escapedNote = note ? escapeHtml(note) : undefined; + const subject = template.subject.replace('{{template_name}}', escapedTemplateName); const greeting = template.body.greeting; - const thanksMessage = template.body.thanksMessage.replace('{{template_name}}', templateName); + const thanksMessage = template.body.thanksMessage.replace('{{template_name}}', escapedTemplateName); const reviewMessage = template.body.reviewMessage; const templateLinkLabel = template.body.templateLinkLabel; const discordMessage = template.body.discordMessage; const discordLink = template.body.discordLink; const closingThanks = template.body.closingThanks; const closingMessage = template.body.closingMessage; return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>${subject}</title> </head> <body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5; line-height: 1.6; color: #1c1f23;"> <table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f5f5f5; padding: 20px 0;"> <tr> <td align="center" style="padding: 20px 0;"> <table role="presentation" style="width: 100%; max-width: 600px; background-color: #ffffff; border-radius: 8px; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> <tr> <td style="padding: 32px 32px 24px 32px;"> <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${greeting}</p> <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${thanksMessage}</p> <p style="margin: 0 0 16px 0; font-size: 16px; color: #1c1f23;">${reviewMessage}</p> <p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;"> ${templateLinkLabel} - <a href="${templateLink}" style="color: #155EEF; text-decoration: none; word-break: break-all;">${templateLink}</a> + <a href="${escapedTemplateLink}" style="color: #155EEF; text-decoration: none; word-break: break-all;">${escapedTemplateLink}</a> </p> - ${note ? `<p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;">${note}</p>` : ''} + ${escapedNote ? `<p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;">${escapedNote}</p>` : ''} <p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;">${discordMessage}</p> <p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;">${discordLink}</p> <p style="margin: 0 0 16px 0; font-size: 14px; color: #1c1f23;">${closingThanks}</p> <p style="margin: 0; font-size: 14px; color: #1c1f23;">${closingMessage}</p> </td> </tr> </table> </td> </tr> </table> </body> </html> `; }
🧹 Nitpick comments (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
68-68: Consider proper typing instead of type assertion.The type assertion
(body as any).publishToCommunitybypasses TypeScript's type checking. SinceCreateWorkflowAppRequestalready includespublishToCommunity?: boolean(as seen in the relevant code snippets), this assertion is unnecessary and reduces type safety.Apply this diff to remove the type assertion:
- const publishToCommunity = (body as any).publishToCommunity ?? false; + const publishToCommunity = body.publishToCommunity ?? false;Also apply the same pattern to other type assertions on lines 66-67 and 69 if their types are properly defined in
CreateWorkflowAppRequest.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/api/src/modules/workflow-app/email-templates.ts(1 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
packages/ai-workspace-common/src/requests/types.gen.ts (2)
User(474-483)CreateWorkflowAppRequest(6612-6649)apps/api/src/modules/workflow-app/email-templates.ts (2)
generateWorkflowAppReviewEmailHTML(45-98)WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE(22-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
| reviewMessage: | ||
| "Our team has received your submission and the review process has officially begun. We typically complete the review. You'll receive another email once the review is done.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review message text appears incomplete.
The reviewMessage states "We typically complete the review." without specifying a timeframe. This leaves users uncertain about when to expect results.
Consider completing the sentence with a specific timeline:
reviewMessage: - "Our team has received your submission and the review process has officially begun. We typically complete the review. You'll receive another email once the review is done.", + "Our team has received your submission and the review process has officially begun. We typically complete the review within 3-5 business days. You'll receive another email once the review is done.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| reviewMessage: | |
| "Our team has received your submission and the review process has officially begun. We typically complete the review. You'll receive another email once the review is done.", | |
| reviewMessage: | |
| "Our team has received your submission and the review process has officially begun. We typically complete the review within 3-5 business days. You'll receive another email once the review is done.", |
🤖 Prompt for AI Agents
In apps/api/src/modules/workflow-app/email-templates.ts around lines 27-28, the reviewMessage string is incomplete and leaves users unsure of timing; update the sentence to include a clear timeline (for example: "We typically complete the review within 3–5 business days") or reference a configurable constant/i18n key instead of a vague phrase, and ensure punctuation and grammar are correct so the message reads as a complete helpful sentence. There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/ai-workspace-common/src/components/app-manager/app-card.tsx (1)
10-10: Remove unusedonDeleteparameter from type annotation.The
onDeleteprop is declared in the type annotation but is never destructured or used in the component. Since the delete/unpublish functionality has been removed, this parameter should also be removed from the type signature to avoid confusion about the component's public API.Apply this diff to clean up the type signature:
-export const AppCard = ({ data }: { data: WorkflowApp; onDelete?: () => void }) => { +export const AppCard = ({ data }: { data: WorkflowApp }) => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/ai-workspace-common/src/components/app-manager/app-card.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/ai-workspace-common/src/components/app-manager/app-card.tsx (1)
packages/openapi-schema/src/types.gen.ts (1)
WorkflowApp(6658-6711)
🔇 Additional comments (2)
packages/ai-workspace-common/src/components/app-manager/app-card.tsx (2)
14-16: Verify thatshareIdis always present for displayed workflow apps.The
handleViewfunction usesdata.shareIdto construct the URL, butshareIdis optional in theWorkflowApptype. IfshareIdisundefined, the URL would be/app/undefined. Please verify that all workflow apps displayed in this component are guaranteed to have ashareId, or add a defensive check.If you cannot guarantee
shareIdis present, consider adding a guard:const handleView = () => { + if (!data.shareId) return; window.open(`/app/${data.shareId}`, '_blank'); };
23-29: LGTM! Clean simplification to view-only functionality.The removal of delete/unpublish functionality and simplification to a single "View" action makes this component more focused and aligns well with the PR's introduction of dedicated public workflow pages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/prisma/schema.prisma (1)
539-582: Data migration for the new many-to-many relationship is incomplete and must be addressed before merging.The schema introduces the new
categoriesmany-to-many relation alongside the deprecatedcategoryIdfield, but verification reveals critical gaps:
- Application code still uses only the deprecated
categoryIdfield (template.service.ts, template.dto.ts)- New
categoriesrelation is never referenced or populated- No migration strategy to move existing category associations to
CanvasTemplateCategoryRelation- No
CanvasTemplateCategoryRelationentries will be created for existing or new templatesRequired before merge:
- Create and execute a data migration to populate
CanvasTemplateCategoryRelationwith existing category associations- Update application code to use the new
categoriesrelation instead of deprecatedcategoryId- Establish a timeline for removing deprecated fields (categoryId, category relation)
♻️ Duplicate comments (1)
apps/api/src/modules/workflow-app/workflow-app.service.ts (1)
221-250: Email notification still sent on every update when publishToCommunity=true.Despite a previous review comment marked as "Addressed in commit 4e56cb9," the current code still sends email notifications on every update. The email block (lines 221-250) executes after both create and update paths without checking whether this is a new publish or an update.
This means users receive duplicate review notification emails each time they update an existing app while
publishToCommunityremainstrue.To fix, send emails only on initial publish by either:
- Moving the email logic inside the create branch (
if (!existingWorkflowApp)), OR- Checking if
publishReviewStatustransitioned from'init'to'reviewing'(requires storing previous status)Apply this diff to send email only for new apps:
} + } + + // Send email notification if template is submitted for review (new apps only) + if (!existingWorkflowApp && publishToCommunity && shareId) { try { const origin = this.configService.get<string>('origin')?.split(',')[0] || ''; const templateLink = `${origin}/app/${shareId}`;Alternatively, track status changes:
+ const shouldSendEmail = publishToCommunity && shareId && ( + !existingWorkflowApp || + (existingWorkflowApp.publishReviewStatus === 'init' && publishToCommunity) + ); + - // Send email notification if template is submitted for review - if (publishToCommunity && shareId) { + if (shouldSendEmail) { try {
🧹 Nitpick comments (1)
apps/api/src/modules/share/share-creation.service.ts (1)
1026-1117: LGTM! Well-refactored helper method eliminates code duplication.The
createOrUpdateWorkflowAppSharehelper effectively reduces duplication between regular and template share creation while maintaining clarity through thelogPrefixparameter for logging. The method properly handles both create and update paths.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/api/prisma/schema.prisma(4 hunks)apps/api/src/modules/share/share-creation.service.ts(5 hunks)apps/api/src/modules/workflow-app/workflow-app.service.ts(5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T02:51:12.185Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.185Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field has a `unique` constraint, making `findUnique` the correct and preferred Prisma method when querying by appId alone. Applied to files:
apps/api/prisma/schema.prisma
📚 Learning: 2025-09-10T02:51:12.184Z
Learnt from: mrcfps Repo: refly-ai/refly PR: 1348 File: apps/api/src/modules/workflow-app/workflow-app.service.ts:88-101 Timestamp: 2025-09-10T02:51:12.184Z Learning: In the WorkflowApp model in apps/api/prisma/schema.prisma, the `appId` field is a unique selector, making `findUnique` the correct Prisma method to use when querying by appId. Applied to files:
apps/api/prisma/schema.prisma
🧬 Code graph analysis (2)
apps/api/src/modules/workflow-app/workflow-app.service.ts (2)
packages/ai-workspace-common/src/requests/types.gen.ts (2)
User(474-483)CreateWorkflowAppRequest(6612-6649)apps/api/src/modules/workflow-app/email-templates.ts (2)
generateWorkflowAppReviewEmailHTML(45-98)WORKFLOW_APP_REVIEW_EMAIL_TEMPLATE(22-36)
apps/api/src/modules/share/share-creation.service.ts (2)
packages/openapi-schema/src/types.gen.ts (5)
User(474-483)WorkflowApp(6658-6711)SharedCanvasData(2520-2525)ShareRecord(3301-3338)CreateShareRequest(3409-3446)packages/errors/src/errors.ts (1)
ShareNotFoundError(203-209)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build / Build
🔇 Additional comments (11)
apps/api/prisma/schema.prisma (3)
399-404: LGTM! Well-defined review status enum.The enum values clearly represent the review workflow states for community publishing.
609-631: LGTM! Well-structured junction table for many-to-many relationship.The model follows best practices with:
- Composite unique constraint preventing duplicate associations
- Indexes on both foreign keys for efficient bidirectional lookups
- Soft delete support
- Proper column name mappings
837-887: LGTM! WorkflowApp model properly extended for community publishing.The new fields appropriately support the publishing workflow:
templateShareIdenables independent template sharing (separate from regularshareId)publishToCommunityflag controls publishing intentpublishReviewStatustracks review state using the new enumremarksprovides admin review feedback capability- Index on
templateShareIdensures efficient lookupsapps/api/src/modules/workflow-app/workflow-app.service.ts (6)
25-30: LGTM! Imports properly support email notification functionality.
60-62: LGTM! Dependencies properly injected.
87-93: LGTM! Credit usage calculation properly applies markup.The calculation correctly applies the execution credit markup from configuration and rounds up to prevent undercharging. This is consistent with the calculation in
share-creation.service.ts(lines 1156-1167).
143-161: Verify that review status transitions are intentional on updates.The code sets
publishReviewStatusto'reviewing'wheneverpublishToCommunityis true during updates (line 156). This means:
- Updating an already-published app with
publishToCommunity=trueresets the status to'reviewing'- Admin review decisions (
approved/rejected) can be overwritten by subsequent updatesIf this is intentional (e.g., re-review required after changes), it's fine. Otherwise, consider preserving the existing
publishReviewStatusunless transitioning from'init'state.
163-182: LGTM! Create path properly initializes new fields.
184-219: LGTM! Share creation properly handles both regular and template shares.The logic correctly:
- Creates both share types when needed
- Updates the WorkflowApp record with both IDs
- Logs creation of each share type
- Handles errors gracefully without failing the workflow app creation
apps/api/src/modules/share/share-creation.service.ts (2)
5-5: LGTM! Import properly supports typing.
1119-1243: Nested share records are incorrectly shared between regular and template shares—the WHERE clause doesn't enforce independence.The review comment's concern about duplication is incorrect. The actual issue is the opposite: nested entities are reused, not duplicated.
The WHERE clause for finding existing share records uses
(entityId, entityType, uid, deletedAt)only—it omitsshareIdorparentShareId. WhenprocessCanvasForShareis called the second time withtemplateShareId, it finds and reuses the same nested share records created by the first call. This violates the stated design intent (lines 1210, 1227) to create independent template shares.Fix: Update the WHERE clause in
createShareForDocument,createShareForResource,createShareForSkillResponse, andcreateShareForCodeArtifactto includeshareId(orparentShareIdwhere applicable) to ensure each parent share has independent nested records.Likely an incorrect or invalid review comment.
Summary
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Tip
Close issue syntax:
Fixes #<issue number>orResolves #<issue number>, see documentation for more details.Impact Areas
Please check the areas this PR affects:
Screenshots/Videos
Checklist
Important
Please review the checklist below before submitting your pull request.
dev/reformat(backend) andcd web && npx lint-staged(frontend) to appease the lint godsSummary by CodeRabbit
New Features
UI
API Changes
Localization
Backend