8000 Nc feat/prompt by rameshmane7218 · Pull Request #9956 · nocodb/nocodb · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Nc feat/prompt #9956

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

Closed
wants to merge 0 commits into from
Closed

Nc feat/prompt #9956

wants to merge 0 commits into from

Conversation

rameshmane7218
Copy link
Member

Change Summary

Provide summary of changes with issue number if any.

Change type

  • feat: (new feature for the user, not a new feature for build script)
  • fix: (bug fix for the user, not a fix to a build script)
  • docs: (changes to the documentation)
  • style: (formatting, missing semi colons, etc; no production code change)
  • refactor: (refactoring production code, eg. renaming a variable)
  • test: (adding missing tests, refactoring tests; no production code change)
  • chore: (updating grunt tasks etc; no production code change)

Test/ Verification

Provide summary of changes.

Additional information / screenshots (optional)

Anything for maintainers to be made aware of

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Dec 4, 2024
Copy link
Contributor
coderabbitai bot commented Dec 4, 2024
📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes in this pull request enhance several components within the nc-gui package, focusing on user interaction, data handling, and visual feedback. Key modifications include the introduction of new props like readOnly and isEditColumn, enhancements to AI-related functionalities, and improvements in styling and tooltip management. Various components, such as PromptWithFields.vue, Settings.vue, and AI.vue, have been updated to improve user experience and maintainability, including refined error handling and the integration of AI features.

Changes

File Path Change Summary
packages/nc-gui/components/ai/PromptWithFields.vue Added readOnly prop to toggle editable state; improved mention suggestion filtering; added tooltip management with tippy.js; updated styling for buttons and prompt fields.
packages/nc-gui/components/ai/Settings.vue Introduced isEditColumn prop; updated availableModels type; added placeholders in NcSelect components; modified model selection logic.
packages/nc-gui/components/cell/AI.vue Integrated isUIAllowed for user permissions; added computed property isDisabledAiButton; simplified AI integration checks; updated button styling.
packages/nc-gui/components/cell/TextArea.vue Introduced injected meta dependency; added computed properties for AI generation state; refined visibility logic for text area.
packages/nc-gui/components/cell/attachment/index.vue Added openAttachmentModal method; enhanced error handling for attachments; updated template and styling for buttons.
packages/nc-gui/components/general/IntegrationIcon.vue Changed size prop to optional; default value set to 'sm'.
packages/nc-gui/components/general/Loader.vue Introduced GeneralLoaderProps interface for props; updated defineProps usage.
packages/nc-gui/components/nc/Button.vue Added loaderSize prop to interface; updated loader size binding.
packages/nc-gui/components/nc/List/RecordItem.vue Improved rendering logic for rows; optimized computed properties for performance.
packages/nc-gui/components/nc/Switch.vue Enhanced visual feedback for disabled state; updated class bindings.
packages/nc-gui/components/smartsheet/Cell.vue Introduced cellType computed property for rendering logic.
packages/nc-gui/components/smartsheet/Gallery.vue Updated context menu functionality; improved error handling in attachments function.
packages/nc-gui/components/smartsheet/Kanban.vue Enhanced context menu and stack operations logic; added computed property hideEmptyStack.
packages/nc-gui/components/smartsheet/PlainCell.vue Updated handling for AI prompt columns; refined text area value logic.
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue Added is-edit-column prop; refined output field options logic.
packages/nc-gui/components/smartsheet/column/ButtonOptions.vue Updated default theme and validation logic for AI buttons.
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue Enhanced handling of AI prompt functionalities; added computed property for AI prompt check.
packages/nc-gui/components/smartsheet/column/LongTextOptions.vue Updated prop handling to modelValue; enhanced AI text generation logic.
packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue Modified filtering logic for options based on feature flags.
packages/nc-gui/components/smartsheet/details/Fields.vue Added onAiFieldAdd method for AI field integration; refined existing field management methods.
packages/nc-gui/components/smartsheet/expanded-form/index.vue Updated copyRecordUrl function with user feedback mechanism; refined state management for modals.
packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue Updated AI-related functionalities and checks.
packages/nc-gui/components/smartsheet/grid/Table.vue Enhanced AI-related functionalities and checks.
packages/nc-gui/components/smartsheet/header/Cell.vue Updated columnTypeName logic for AI buttons.
packages/nc-gui/components/smartsheet/header/CellIcon.ts Added condition for AI columns in renderIcon function.
packages/nc-gui/components/smartsheet/header/Menu.vue Introduced columnInvalid computed property for column validity checks.
packages/nc-gui/components/smartsheet/header/VirtualCell.vue Updated columnTypeName logic for AI buttons.
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue Enhanced user feedback for view ID copy action; updated lock type change logic.
packages/nc-gui/components/virtual-cell/Button.vue Modified button styling; retained existing functionality.
packages/nc-gui/components/virtual-cell/HasMany.vue Updated icon properties for better visual representation.
packages/nc-gui/components/virtual-cell/ManyToMany.vue Enhanced dialog state management with new checks.
packages/nc-gui/components/virtual-cell/components/ListItem.vue Replaced MaximizeIcon with GeneralIcon for consistency.
packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue Removed loadAiIntegrations function for simplified deletion process.
packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue Added loadDynamicIntegrations method for dynamic integration loading.
packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue Updated import statements for type clarity; removed loadAiIntegrations call.
packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue Updated button size and introduced textarea for connection URL input.
packages/nc-gui/components/workspace/project/AiCreateProject.vue Enhanced AI schema generation functionality; updated prompt construction logic.
packages/nc-gui/composables/useColumnCreateStore.ts Added isAIPromptCol import; updated isAiMode logic.
packages/nc-gui/composables/useData.ts Integrated isAIPromptCol into updateRowProperty function.
packages/nc-gui/composables/useInfiniteData.ts Added isAIPromptCol import; updated updateRowProperty logic.
packages/nc-gui/composables/useIntegrationsStore.ts Introduced loadDynamicIntegrations function; refined integration management.
packages/nc-gui/composables/useMultiSelect/index.ts Added AIRecordType type for handling AI records.
packages/nc-gui/composables/useNocoAi.ts Updated aiIntegrations type; added aiIntegrationAvailable computed property.
packages/nc-gui/helpers/parsers/parserHelpers.ts Updated generateUniqueColumnName logic for AI buttons and long text.
packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue Enhanced keyboard navigation and UI updates.
packages/nc-gui/utils/cell.ts Improved isAI function logic for identifying AI columns.
packages/nc-gui/utils/columnUtils.ts Enhanced isColumnInvalid function to handle AI integrations.
packages/nc-gui/utils/commonUtils.ts Introduced ncSearchCompare function for case-insensitive search.
packages/nocodb-sdk/src/lib/UITypes.ts Added constants and function for AI functionalities.
packages/nocodb-sdk/src/lib/globals.ts Introduced LongTextAiMetaProp constant.
packages/nocodb-sdk/src/lib/helperFunctions.ts Added parseHelper and stringifyHelper functions for JSON handling.
packages/nocodb-sdk/src/lib/index.ts Exported isAIPromptCol function.
packages/nocodb/src/db/BaseModelSqlv2.ts Updated prepareNocoData method for AI prompt handling.
packages/nocodb/src/db/conditionV2.ts Integrated isAIPromptCol into parseConditionV2 function.
packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts Enhanced handling of LongText data type for AI prompts.
packages/nocodb/src/db/sortV2.ts Added case for handling UITypes.LongText in sorting logic.
packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts Introduced new migration source for AI prompts.
packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts Defined migration functions for AI prompt table creation.
packages/nocodb/src/models/AIColumn.ts Introduced AIColumn class for managing AI-related columns.
packages/nocodb/src/models/Column.ts Enhanced column management for AI-related functionalities.
packages/nocodb/src/models/Integration.ts Improved validation and resource management during integration deletion.
packages/nocodb/src/models/LongTextColumn.ts Introduced abstract class for managing long text columns.
packages/nocodb/src/models/index.ts Exported LongTextColumn and AIColumn classes.
packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts Updated duplicateColumn method for AI prompt handling.
packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts Enhanced export service for handling long text fields.
packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts Updated import logic to filter AI prompt columns.
packages/nocodb/src/services/columns.service.ts Enhanced column management for AI prompt columns.
packages/nocodb/src/utils/dataConversion.ts Introduced functions for converting AI record types.
packages/nocodb/src/utils/globals.ts Updated enums to reflect changes in column classifications.

Possibly related PRs

Suggested labels

👓 Scope : View, size:M, lgtm

Suggested reviewers

  • dstala

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot dosubot bot added the 🔦 Type: Feature New functionality is requested. label Dec 4, 2024
@rameshmane7218 rameshmane7218 requested a review from dstala December 4, 2024 08:09
Copy link
Contributor
github-actions bot commented Dec 4, 2024

Uffizzi Preview deployment-58800 was deleted.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

🧹 Outside diff range and nitpick comments (54)
packages/nc-gui/components/nc/Switch.vue (1)

45-47: LGTM! Consider adding ARIA attributes for better accessibility.

The addition of opacity-60 class for disabled states follows best practices by providing clear visual feedback. The implementation is consistent across both placement options and correctly pairs with cursor-not-allowed.

Consider enhancing accessibility by adding ARIA attributes to the clickable spans:

 <span
   v-if="placement === 'right' && $slots.default"
   class="pr-2"
+  :aria-disabled="disabled"
+  role="button"
   :class="[
     contentWrapperClass,
     {
       'cursor-not-allowed opacity-60': disabled,
       'cursor-pointer': !disabled,
     },
   ]"
   @click="onChange(!checked, true)"
 >

Apply the same changes to the left placement span as well.

Also applies to: 72-74

packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (1)

Line range hint 666-681: Consider using v-focus directive for better reusability.

While the current implementation of auto-focus using ref callback works, consider creating a reusable v-focus directive for consistency across the application.

Here's how you could implement it:

+ // In a shared directives file
+ export const vFocus = {
+   mounted: (el) => el.focus()
+ }

+ // In the component
+ directives: {
+   focus: vFocus
+ }

- :ref="
-   (el) => {
-     el?.$el?.focus()
-   }
- "
+ v-focus

This approach would:

  1. Make the focus behavior reusable across components
  2. Reduce inline callback complexity
  3. Follow Vue's best practices for DOM manipulation
packages/nc-gui/components/general/Loader.vue (3)

4-7: LGTM! Consider adding JSDoc comments.

The interface is well-structured with appropriate types. The optional props provide good flexibility.

Consider adding JSDoc comments to document the purpose and usage of each prop:

 export interface GeneralLoaderProps {
+  /** Size of the loader. Can be predefined sizes or a custom number in pixels */
   size?: 'small' | 'medium' | 'large' | 'xlarge' | 'regular' | number
+  /** Additional CSS classes to apply to the loader */
   loaderClass?: string
 }

Line range hint 9-27: Consider handling numeric size in getFontSize function.

While the function handles all string literal cases, it doesn't account for when size is a number. The number case is only handled in the template, which could lead to inconsistent behavior.

Consider updating the function to handle numeric sizes:

 function getFontSize() {
   const { size = 'medium' } = props
 
+  if (typeof size === 'number') {
+    return `text-[${size}px]`
+  }
+
   switch (size) {
     case 'small':
       return 'text-xs'
     case 'medium':

Line range hint 29-32: Fix syntax error in class string concatenation.

There's an extra closing curly brace in the class string that could cause styling issues.

Apply this fix:

 const indicator = h(LoadingOutlined, {
-  class: `!${getFontSize()} flex flex-row items-center !bg-inherit !hover:bg-inherit !text-inherit ${props.loaderClass || ''}}`,
+  class: `!${getFontSize()} flex flex-row items-center !bg-inherit !hover:bg-inherit !text-inherit ${props.loaderClass || ''}`,
   style: { ...(typeof props.size === 'number' && props.size ? { fontSize: `${props.size}px` } : {}) },
   spin: true,
 })
packages/nc-gui/components/smartsheet/header/Menu.vue (3)

52-61: Consider enhancing type safety

While the computed property is well-structured, consider defining an interface for the return type instead of using an inline type.

+interface ColumnValidation {
+  isInvalid: boolean
+  tooltip: string
+}

-const columnInvalid = computed<{ isInvalid: boolean; tooltip: string }>(() => {
+const columnInvalid = computed<ColumnValidation>(() => {

431-434: Extract magic number to a named constant

The 5000ms delay should be extracted to a named constant for better maintainability.

+const COPY_FEEDBACK_DURATION_MS = 5000
+
 const  (field: ColumnType) => {
   await copy(field.id!)
   isFieldIdCopied.value = true
-  await ncDelay(5000)
+  await ncDelay(COPY_FEEDBACK_DURATION_MS)
   isFieldIdCopied.value = false
 }

451-451: Consider using more specific gap sizing

The generic gap-1 utility class could be replaced with a more specific value to ensure consistent spacing across different screen sizes.

-    <div class="flex gap-1 items-center" @dblclick.stop>
+    <div class="flex gap-x-2 items-center" @dblclick.stop>

Also applies to: 461-461, 464-464

packages/nc-gui/components/workspace/project/AiCreateProject.vue (3)

150-162: Consider using a more robust prompt construction method.

The current string concatenation approach could lead to malformed prompts if the optional fields contain special characters or formatting that could affect the prompt structure.

Consider using an array to collect the parts and then joining them:

-  let prompt = `${aiFormState.value.prompt}`
-
-  // Append optional information if provided
-  if (aiFormState.value.organization?.trim()) {
-    prompt += ` | Organization: ${aiFormState.value.organization}`
-  }
-  if (aiFormState.value.industry?.trim()) {
-    prompt += ` | Industry: ${aiFormState.value.industry}`
-  }
-  if (aiFormState.value.audience?.trim()) {
-    prompt += ` | Audience: ${aiFormState.value.audience}`
-  }
+  const promptParts = [aiFormState.value.prompt.trim()]
+  
+  if (aiFormState.value.organization?.trim()) {
+    promptParts.push(`Organization: ${aiFormState.value.organization.trim()}`)
+  }
+  if (aiFormState.value.industry?.trim()) {
+    promptParts.push(`Industry: ${aiFormState.value.industry.trim()}`)
+  }
+  if (aiFormState.value.audience?.trim()) {
+    promptParts.push(`Audience: ${aiFormState.value.audience.trim()}`)
+  }
+  
+  const prompt = promptParts.join(' | ')

Line range hint 164-207: Ensure timer cleanup on component unmount.

The timerId interval might continue running if the component is unmounted during the loading state, potentially causing memory leaks.

Add cleanup logic in the onBeforeUnmount lifecycle hook:

+onBeforeUnmount(() => {
+  if (timerId) {
+    clearInterval(timerId)
+  }
+})

Line range hint 385-397: Enhance accessibility for interactive elements.

The collapse panels and buttons could benefit from improved accessibility attributes.

Add ARIA attributes to improve screen reader support:

 <a-collapse-panel
   v-for="table in predictedSchema.tables"
   :key="table.title"
   collapsible="disabled"
+  :aria-label="`Table: ${table.title}`"
 >
   <template #header>
     <div
       class="w-full flex items-center px-4 py-2"
+      role="button"
+      :aria-expanded="previewExpansionPanel.includes(table.title)"
       @click="handleUpdatePreviewExpansionPanel(table.title, !viewsGrouped[table.title].length)"
     >
packages/nc-gui/utils/commonUtils.ts (1)

74-93: LGTM! Consider a minor optimization.

The implementation is well-documented and handles edge cases gracefully. However, we could optimize it slightly by caching the lowercase values.

Here's an optimized version that avoids calling toLowerCase() twice when both inputs are defined:

 export const ncSearchCompare = (source?: string, query?: string): boolean => {
-  return (source || '').toLowerCase().includes((query || '').toLowerCase())
+  const normalizedSource = (source || '').toLowerCase()
+  const normalizedQuery = (query || '').toLowerCase()
+  return normalizedSource.includes(normalizedQuery)
 }
packages/nc-gui/components/nc/List/RecordItem.vue (2)

Line range hint 39-49: Consider enhancing error handling in attachments parsing.

The current implementation silently fails when JSON parsing errors occur. Consider logging these errors or handling specific error types for better debugging and maintenance.

 const attachments: ComputedRef<Attachment[]> = computed(() => {
   try {
     if (attachmentColumn.value?.title && currentRow.value.row[attachmentColumn.value.title]) {
       return typeof currentRow.value.row[attachmentColumn.value.title] === 'string'
         ? JSON.parse(currentRow.value.row[attachmentColumn.value.title])
         : currentRow.value.row[attachmentColumn.value.title]
     }
     return []
   } catch (e) {
+    console.warn(`Failed to parse attachments for column ${attachmentColumn.value?.title}:`, e)
     return []
   }
 })

Line range hint 144-168: Consider extracting the column rendering logic into a separate component.

The nested template logic for rendering columns is complex and could be extracted into a dedicated component to improve maintainability and reusability.

Consider creating a new component like ListItemColumn.vue to handle the column rendering logic. This would:

  • Improve code organization
  • Make the template easier to understand
  • Enable better testing of the column rendering logic
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (4)

155-158: Consider reducing the copy confirmation delay

The 5-second delay for the copy confirmation might be longer than necessary for good UX. Consider reducing it to 2-3 seconds, which is typically sufficient for users to notice the confirmation while maintaining responsiveness.

- await ncDelay(5000)
+ await ncDelay(2000)

Line range hint 196-207: Move implementation note to component documentation

The detailed note about v-if vs v-show implementation should be moved to component-level documentation or a separate docs file, as it's more of a general implementation guideline than runtime code.

Consider creating a CONTRIBUTING.md or similar documentation file for such implementation guidelines.


Line range hint 78-116: Enhance error handling in changeLockType

The error handling could be more specific to help with debugging and user feedback.

  try {
    view.value.lock_type = type
    await $api.dbView.update(view.value.id as string, {
      lock_type: type,
    })

    message.success(`Successfully Switched to ${type} view`)
  } catch (e: any) {
-   message.error(await extractSdkResponseErrorMsg(e))
+   const errorMsg = await extractSdkResponseErrorMsg(e)
+   message.error(`Failed to change view lock type: ${errorMsg}`)
+   // Revert the optimistic update
+   view.value.lock_type = lockType.value
  }

Line range hint 78-116: Extract lock-related messages to constants

Consider extracting the lock-related messages and magic strings to constants for better maintainability and reusability.

const LOCK_MESSAGES = {
  ALREADY_IN_STATE: (type: string) => `Already in ${type} view`,
  SWITCH_SUCCESS: (type: string) => `Successfully Switched to ${type} view`,
  DEFAULT_VIEW_ERROR: 'Not allowed to change default view',
} as const
packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (2)

Line range hint 71-79: Replace setTimeout with Vue's nextTick mechanism.

The current implementation uses setTimeout for input focus, which is noted as a TODO. Consider using Vue's built-in mechanisms.

 nextTick(() => {
-  // todo: replace setTimeout and follow better approach
-  setTimeout(() => {
-    const input = form.value?.$el?.querySelector('input[type=text]')
-    input?.setSelectionRange(0, formState.value.title.length)
-    input?.focus()
-  }, 500)
+  const input = form.value?.$el?.querySelector('input[type=text]')
+  if (input) {
+    input.setSelectionRange(0, formState.value.title.length)
+    input.focus()
+  }
 })

Line range hint 35-46: Enhance error handling in the submit function.

The current error handling only logs to console. Consider implementing proper error feedback to users.

 try {
   if (pageMode.value === IntegrationsPageMode.ADD) {
     await saveIntegration(formState.value)
   } else {
     await updateIntegration({
       id: activeIntegration.value?.id,
       ...formState.value,

10000
     })
   }
 } catch (e) {
-  console.error(e)
+  const errorMessage = e instanceof Error ? e.message : 'Failed to save integration'
+  // Assuming you have a notification system
+  useNotification.error({
+    title: 'Error',
+    message: errorMessage
+  })
+  throw e // Re-throw to prevent closing the form on error
 } finally {
   isLoading.value = false
 }
packages/nc-gui/components/cell/TextArea.vue (1)

Line range hint 423-446: Refactor duplicated code to improve maintainability

The code displaying the "Edited by" message is duplicated in both the tooltip title and the display text. Refactoring this logic into a computed property or a method would reduce code duplication and enhance maintainability.

For example, create a computed property:

const editedByText = computed(() => {
  if (user?.id === props.aiMeta?.lastModifiedBy) {
    return 'you'
  }
  const editor = idUserMap[props.aiMeta?.lastModifiedBy]
  return editor?.display_name || editor?.email || 'unknown user'
})

Then update the template:

<NcTooltip v-if="isAiEdited" class="text-nc-content-green-dark flex-1 truncate" show-on-truncate-only>
  <template #title>Edited by you</template>
  Edited by you
</NcTooltip>
<NcTooltip v-else-if="props.aiMeta?.lastModifiedBy" class="text-nc-content-green-dark flex-1 truncate" show-on-truncate-only>
  <template #title>Edited by {{ editedByText }}</template>
  Edited by {{ editedByText }}
</NcTooltip>
packages/nc-gui/composables/useIntegrationsStore.ts (3)

300-309: Simplify object construction using object spread

When adding a new AI integration, you can simplify the object by spreading response if it contains only necessary properties.

Apply this diff to simplify the code:

if (response.type === IntegrationsType.Ai) {
-  aiIntegrations.value.push({
-    id: response.id,
-    title: response.title,
-    is_default: response.is_default,
-    type: response.type,
-    sub_type: response.sub_type,
-  })
+  aiIntegrations.value.push({
+    id: response.id,
+    ...response,
+  })
}

Ensure that response does not contain any sensitive or unnecessary data before spreading it.


Line range hint 447-496: Declare integrationsInitialized and allIntegrations before use

The variables integrationsInitialized and allIntegrations are used but not declared within the scope of this composable, which will lead to ReferenceError.

Declare these variables at the appropriate scope:

+ const integrationsInitialized = ref(false)
+ const allIntegrations = ref<IntegrationItemType[]>([])

Ensure that these variables are reactive to maintain reactivity in your application.


Line range hint 447-496: Handle undefined icon when neither condition is met

If di.meta.icon is neither in iconMap nor a valid URL, icon may remain undefined, leading to potential rendering issues.

Ensure icon always has a valid value:

if (di.meta.icon) {
  // existing logic
} else {
  icon = iconMap.puzzle || h('DefaultIconComponent')
}

Alternatively, set a default icon component if iconMap.puzzle is undefined.

packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3)

Line range hint 89-94: Clean up method signature and enhance error handling

The _e parameter is unused and should be removed. Additionally, consider adding error handling for invalid indices.

- selectItem(index, _e) {
+ selectItem(index) {
    const item = this.items[index]

+   if (index < 0 || index >= this.items.length) {
+     console.warn('Invalid index provided to selectItem')
+     return
+   }

    if (item) {
      this.command({
        id: item.title,
      })
    }
  }

102-102: Optimize template structure and z-index management

The template has some redundancy and a hardcoded z-index value.

Consider these improvements:

  1. Extract the title to a computed property to avoid repetition
  2. Consider using CSS variables for z-index management
+ computed: {
+   getItemTitle() {
+     return (item) => item?.title || ''
+   }
+ }

  <NcTooltip 
    class="truncate" 
    show-on-truncate-only 
-   :tooltip-style="{ zIndex: '10000' }"
+   :tooltip-style="{ zIndex: 'var(--nc-tooltip-z-index, 10000)' }"
  >
    <template #title>
-     {{ item?.title || '' }}
+     {{ getItemTitle(item) }}
    </template>
-   {{ item?.title || '' }}
+   {{ getItemTitle(item) }}
  </NcTooltip>

Also applies to: 110-110, 114-119


Line range hint 1-133: Consider enhancing component architecture

While the changes improve the UI/UX, consider these architectural improvements:

  1. Extract keyboard handling logic into a separate composable for better reusability
  2. Add proper cleanup in component destruction to prevent memory leaks
  3. Consider using Vue's provide/inject for z-index management across components

Would you like assistance in implementing any of these architectural improvements?

packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

41-45: Correct typo in variable name isFieldAddedInPromt

The variable isFieldAddedInPromt contains a typo. It should be isFieldAddedInPrompt to reflect the correct spelling.

Apply this diff to fix the typo:

const isPreviewEnabled = computed(() => {
-  const isFieldAddedInPromt = availableFields.value.some((f) => {
+  const isFieldAddedInPrompt = availableFields.value.some((f) => {
     return vModel.value.prompt_raw?.includes(`{${f.title}}`)
   })

-  return isFieldAddedInPromt
+  return isFieldAddedInPrompt
})

108-109: Avoid unnecessary type assertion

The type assertion (column?.value?.colOptions as Record<string, any>) may be unnecessary and could mask potential undefined values. Consider accessing prompt_raw directly with optional chaining.

Apply this diff to simplify the code:

- vModel.value.prompt_raw = (column?.value?.colOptions as Record<string, any>)?.prompt_raw || ''
+ vModel.value.prompt_raw = column?.value?.colOptions?.prompt_raw || ''
packages/nc-gui/components/smartsheet/calendar/MonthView.vue (3)

Line range hint 186-260: Optimize the findAvailableLane and occupyLane functions

The findAvailableLane and occupyLane functions use nested loops which could affect performance with a large number of records. Consider optimizing these functions to improve performance in large datasets.


Line range hint 429-511: Fix incorrect date calculations in onResize method

In the onResize method, when resizing an event, the calculation of newEndDate and newStartDate may lead to incorrect dates if the user drags beyond the limits. Ensure that date boundaries are correctly handled to prevent invalid date assignments.


Line range hint 933-939: Ensure fieldStyles[field.id] exists before accessing

In the template, when using fieldStyles[field.id], there is no check to ensure fieldStyles[field.id] exists. This could lead to runtime errors if fieldStyles does not contain the field.id. Consider adding a default value or a check before accessing properties.

Apply this diff to add a default value:

<LazySmartsheetPlainCell
  v-model="record.row[field!.title!]"
  class="text-xs"
- :bold="fieldStyles[field.id].bold"
- :italic="fieldStyles[field.id].italic"
- :underline="fieldStyles[field.id].underline"
+ :bold="fieldStyles[field.id]?.bold"
+ :italic="fieldStyles[field.id]?.italic"
+ :underline="fieldStyles[field.id]?.underline"
/>
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (2)

Line range hint 53-57: Initialize vModel.value.meta before assignment

When setting richMode in the computed property, vModel.value.meta might be undefined. Assigning vModel.value.meta.richMode = value without checking can cause errors.

Apply this diff to fix the issue:

set: (value) => {
+  if (!vModel.value.meta) vModel.value.meta = {}
   vModel.value.meta.richMode = value
},

Line range hint 687-693: Simplify the conditional classes

The class bindings in the nc-ai-button-test-generate-wrapper can be simplified for readability.

Apply this diff to simplify the class bindings:

:class="{
  'text-nc-border-gray-dark': !(selectedRecordPk && outputColumnIds.length && inputColumns.length),
  'text-nc-content-purple-dark': selectedRecordPk && outputColumnIds.length && inputColumns.length,
}"
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (1)

1065-1078: Simplify the conditional rendering of icons

The :is binding in the component has nested ternary operators which can be complex to read. Consider simplifying or extracting the logic for better readability.

packages/nc-gui/components/smartsheet/Kanban.vue (1)

1146-1146: Incorrect icon used in context menu

In the context menu, the icon for expanding a record has been changed to iconMap.maximize. Ensure that this icon accurately represents the action and is consistent with the UI guidelines.

packages/nocodb/src/services/columns.service.ts (1)

2266-2288: Ensure prompt is properly assigned when prompt_raw is missing

Currently, if colBody.prompt_raw is undefined, prompt remains an empty string, leading to colBody.prompt being set to an empty string. Consider setting colBody.prompt only when prompt_raw is provided to prevent unintended empty prompts.

packages/nocodb/src/db/BaseModelSqlv2.ts (1)

6506-6508: Remove unused parameters in handleRichTextMentions method

The parameters _prevData, _newData, and _req are unused in the handleRichTextMentions method. Consider removing them to clean up the code.

Apply this diff to clean up the method signature:

- public async handleRichTextMentions(
-   _prevData,
-   _newData: Record<string, any> | Array<Record<string, any>>,
-   _req,
- ) {
+ public async handleRichTextMentions() {
    return;
  }
packages/nc-gui/components/smartsheet/header/CellIcon.ts (1)

23-24: LGTM! Consider adding type safety for iconMap

The addition of AI column type handling is implemented correctly. However, consider adding type safety for iconMap.cellAi to ensure it exists at compile time.

+ // Add at the top of the file with other type definitions
+ interface IconMap {
+   cellAi: Component;
+   cellSystemKey: Component;
+   // ... other icon types
+ }
+ 
+ const iconMap: IconMap = {
+   // ... icon mappings
+ }
packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (1)

23-25: LGTM! Consider extracting AI types to a constant

The feature flag check correctly handles both AI-related types. Consider extracting these types to a constant for better maintainability.

+ const AI_TYPES = ['AIButton', 'AIPrompt'] as const;
+ 
  const filteredOptions = computed(
    () =>
      options.value?.filter(
        (c) =>
-         !((c.name === 'AIButton' || c.name === 'AIPrompt') && !isFeatureEnabled(FEATURE_FLAG.AI_FEATURES)) &&
+         !(AI_TYPES.includes(c.name) && !isFeatureEnabled(FEATURE_FLAG.AI_FEATURES)) &&
          (c.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
            (UITypesName[c.name] && UITypesName[c.name].toLowerCase().includes(searchQuery.value.toLowerCase()))),
      ) ?? [],
  )
packages/nc-gui/components/cell/AI.vue (1)

131-139: Simplify tooltip disabled condition

The tooltip disabled condition isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit') is complex and might be confusing.

Consider using the existing computed property:

-    <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+    <NcTooltip :disabled="!isDisabledAiButton" class="flex">
packages/nocodb-sdk/src/lib/helperFunctions.ts (1)

231-245: Consider adding debug logging for parse/stringify failures

The functions silently handle errors, which might make debugging difficult in production.

Consider adding debug logging:

 export function parseHelper(v: any): any {
   try {
     return typeof v === 'string' ? JSON.parse(v) : v
-  } catch {
+  } catch (e) {
+    console.debug('Failed to parse JSON:', e)
     return v
   }
 }

 export function stringifyHelper(v: any): string {
   try {
     return JSON.stringify(v)
-  } catch {
+  } catch (e) {
+    console.debug('Failed to stringify value:', e)
     return v
   }
 }
packages/nc-gui/components/ai/PromptWithFields.vue (3)

63-68: Consider optimizing the filter condition.

The filter condition could be simplified for better readability and performance.

Consider this optimization:

-return (
-  props.options?.filter(
-    (o) =>
-      o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
-  ) ?? []
-)
+const lowerQuery = query.toLowerCase()
+return props.options?.filter((o) => {
+  const lowerTitle = o.title?.toLowerCase()
+  return lowerTitle?.includes(lowerQuery) || `${lowerTitle}}` === lowerQuery
+}) ?? []

159-174: Consider debounce timing adjustment.

The tooltip implementation is good, but the debounce timing (1000ms) might be too long for a smooth user experience.

Consider reducing the debounce timing:

-const debouncedLoadMentionFieldTagTooltip = useDebounceFn(loadMentionFieldTagTooltip, 1000)
+const debouncedLoadMentionFieldTagTooltip = useDebounceFn(loadMentionFieldTagTooltip, 300)

226-228: Consider browser compatibility.

The margin-right on paragraphs might need a fallback for older browsers.

Consider adding:

 & > p {
   @apply mr-3;
+  margin-right: 0.75rem; /* Fallback for browsers that don't support @apply */
 }
packages/nc-gui/components/ai/Settings.vue (2)

36-36: Consider type safety improvements.

The type casting of the response could be more robust.

Consider adding interface and type guard:

interface AIModel {
  value: string
  label: string
}

function isAIModelArray(data: unknown): data is AIModel[] {
  return Array.isArray(data) && data.every(item => 
    typeof item === 'object' && item !== null &&
    'value' in item && typeof item.value === 'string' &&
    'label' in item && typeof item.label === 'string'
  )
}

// Then use it:
const response = await $api.integrations.endpoint(...)
if (isAIModelArray(response)) {
  availableModels.value = response
} else {
  console.error('Invalid response format')
  availableModels.value = []
}

Also applies to: 57-61


83-86: Consider error handling improvement.

The model initialization logic could benefit from better error handling.

Consider adding error state handling:

 if (!vModel.value || !availableModels.value.length) {
+  try {
     await onIntegrationChange()
+  } catch (error) {
+    console.error('Failed to initialize model:', error)
+    // Consider emitting an error event or showing user feedback
+  }
 }
packages/nc-gui/components/smartsheet/header/Cell.vue (1)

46-55: Consider using the isAIPromptCol helper function

The logic for identifying AI prompt columns could be simplified by using the newly added isAIPromptCol helper function from UITypes.

  if (column.value.uidt === UITypes.LongText) {
    if (parseProp(column.value?.meta)?.richMode) {
      return UITypesName.RichText
    }

-   if (parseProp(column.value?.meta)?.[LongTextAiMetaProp]) {
+   if (isAIPromptCol(column.value)) {
      return UITypesName.AIPrompt
    }
  }
packages/nocodb-sdk/src/lib/UITypes.ts (1)

191-197: Improve type safety in isAIPromptCol

The function uses type assertion (col as any)?.meta which bypasses TypeScript's type checking. Consider using a more type-safe approach.

export function isAIPromptCol(
  col:
    | ColumnReqType
    | ColumnType
) {
-  return col.uidt === UITypes.LongText && parseHelper((col as any)?.meta)?.[LongTextAiMetaProp];
+  return col.uidt === UITypes.LongText && 'meta' in col && parseHelper(col.meta)?.[LongTextAiMetaProp];
}
packages/nc-gui/components/smartsheet/Cell.vue (1)

150-176: Well-structured computed property for cell type determination.

The cellType computed property provides a clean and maintainable way to determine cell types. The checks are properly ordered from specific to general cases.

Consider extracting the type checks into a separate utility function to improve reusability and testability:

+const getCellType = (column: ColumnType, abstractType: any) => {
+  if (isAI(column)) return 'ai'
+  if (isTextArea(column)) return 'textarea'
+  // ... rest of the checks
+  return 'text'
+}

 const cellType = computed(() => {
-  if (isAI(column.value)) return 'ai'
-  if (isTextArea(column.value)) return 'textarea'
-  // ... rest of the checks
-  return 'text'
+  return getCellType(column.value, abstractType.value)
 })
packages/nc-gui/composables/useColumnCreateStore.ts (1)

Line range hint 308-321: Improved error handling with user feedback.

The error handling now provides clear feedback to users when validation fails, enhancing the user experience.

Consider extracting the error handling logic into a separate function for better maintainability:

+const handleValidationErrors = (e: any) => {
+  const errorMsgs = e.errorFields
+    ?.map((e: any) => e.errors?.join(', '))
+    .filter(Boolean)
+    .join(', ')
+
+  if (errorMsgs) {
+    message.error(errorMsgs)
+    return false
+  }
+
+  if (!fromKanbanStack?.value || (fromKanbanStack.value && !e.outOfDate)) {
+    message.error(t('msg.error.formValidationFailed'))
+    return false
+  }
+  return true
+}

 const addOrUpdate = async (onSuccess: () => Promise<void>, columnPosition?: Pick<ColumnReqType, 'column_order'>) => {
   try {
     if (!(await validate())) return
   } catch (e: any) {
-    const errorMsgs = e.errorFields
-      ?.map((e: any) => e.errors?.join(', '))
-      .filter(Boolean)
-      .join(', ')
-
-    if (errorMsgs) {
-      message.error(errorMsgs)
-      return
-    }
-
-    if (!fromKanbanStack?.value || (fromKanbanStack.value && !e.outOfDate)) {
-      message.error(t('msg.error.formValidationFailed'))
-      return
-    }
+    if (!handleValidationErrors(e)) return
   }
   // ... rest of the function
 }
packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (1)

408-408: Consider using let for mutable array

The comments array is being mutated with push operations, but it's declared as a const. While this works because array mutation methods don't reassign the reference, using let would be more semantically correct for a mutable array.

-const comments: Comment[] = [];
+let comments: Comment[] = [];

Also applies to: 411-415

packages/nc-gui/composables/useMultiSelect/index.ts (2)

292-298: Consider adding type safety for AI record value.

The AI content handling logic is well-structured, but consider adding type checking to ensure the value exists in the AIRecordType.

Consider this safer implementation:

 if (parseProp(columnObj.meta)?.[LongTextAiMetaProp] === true) {
   const aiCell: AIRecordType = (columnObj.title && rowObj.row[columnObj.title]) || null

   if (aiCell) {
+    if ('value' in aiCell) {
       textToCopy = aiCell.value
+    }
   }
 }

299-300: Fix indentation in the else block.

The indentation in the else block is inconsistent with the surrounding code style.

Apply this fix:

 } else {
-        textToCopy = `"${textToCopy.replace(/"/g, '\\"')}"`
+  textToCopy = `"${textToCopy.replace(/"/g, '\\"')}"`
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1b68479 and 8c6e440.

📒 Files selected for processing (73)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/dashboard/settings/base/index.vue (0 hunks)
  • packages/nc-gui/components/feed/View.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/calendar/MonthView.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/Sidebar/Audits.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (5 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (11 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (6 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (5 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
  • packages/nc-gui/components/dashboard/settings/base/index.vue
✅ Files skipped from review due to trivial changes (3)
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/feed/View.vue
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (80)
packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (1)

661-662: LGTM: Button styling improvements enhance visual consistency.

The changes to b 8000 utton size and padding improve the visual consistency with other UI elements.

packages/nc-gui/components/smartsheet/header/Menu.vue (1)

48-49: LGTM: Clean AI integration

The injection of aiIntegrations follows Vue's composition API best practices.

packages/nc-gui/components/nc/Button.vue (2)

4-4: LGTM! Well-structured prop addition.

The new loaderSize prop is properly typed and maintains consistency with existing defaults.

Also applies to: 23-23, 37-37


53-53: LGTM! Proper reactive props handling.

The loaderSize prop is correctly destructured using toRefs to maintain reactivity.

packages/nc-gui/components/virtual-cell/Button.vue (1)

213-213: Verify design system consistency for button border radius

The change from rounded-lg to rounded-md reduces the button's corner radius. While this is a valid styling update, please ensure it aligns with the design system and maintains visual consistency with other UI components.

✅ Verification successful

Change to rounded-md is consistent with design system

Based on the codebase analysis, the change from rounded-lg to rounded-md in Button.vue aligns with the design system's border radius usage patterns. The codebase shows:

  1. rounded-md is commonly used for interactive elements like buttons, especially in compact UI components
  2. Most button-like components use rounded-md for consistency:
    • Virtual cell buttons (Button.vue, AI.vue)
    • Action buttons in toolbars and menus
    • Form controls and interactive elements

The change maintains visual consistency with other similar UI components in the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent border radius usage across components
# Look for other instances of rounded-lg or rounded-md in Vue components

echo "Checking border radius consistency across components..."
rg -t vue "rounded-(lg|md)" packages/nc-gui/components/

Length of output: 206


Script:

#!/bin/bash
# Search for rounded-lg or rounded-md in all files under packages/nc-gui
# Using ripgrep without file type filter since .vue extension is not recognized
rg "rounded-(lg|md)" packages/nc-gui/

Length of output: 66785

packages/nc-gui/components/virtual-cell/ManyToMany.vue (1)

148-149: LGTM! Icon and styling changes align with design system.

The changes improve UI consistency by:

  • Using the standardized "maximize" icon
  • Adding explicit sizing classes (h-3 w-3) for better layout control

Let's verify the icon usage pattern across the codebase:

✅ Verification successful

Icon usage pattern is consistent across the codebase

The verification confirms that the change from "expand" to "maximize" icon and the addition of explicit sizing classes (h-3 w-3) aligns with the codebase's icon usage patterns:

  • The "maximize" icon is consistently used for expansion/maximize actions across components
  • Icon sizing classes (h-3 w-3, h-4 w-4) are commonly applied for consistent dimensions
  • The styling approach with hover states and text colors follows the established pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of maximize/expand icons and their sizing

# Check for any remaining "expand" icons that might need updating
rg -A 2 'icon="expand"' --type vue

# Check for consistent sizing classes with maximize icons
rg -A 2 'icon="maximize"' --type vue

# Check for any similar expand/maximize functionality to ensure consistent patterns
ast-grep --pattern 'icon="maximize"' 

Length of output: 65846


Script:

#!/bin/bash
# Let's try a different approach to check icon usage patterns in Vue files

# Search for icon-related files and patterns
fd -e vue -x rg -l "icon=" {} \;

# Search for specific icon names and their classes
fd -e vue -x rg -A 2 -B 2 'icon="\w+"' {} \;

Length of output: 69485

packages/nc-gui/components/nc/List/RecordItem.vue (1)

145-145: Verify the empty row check behavior across different column types.

The change to use currentRow instead of the destructured row prop is correct as it maintains reactivity. However, we should ensure that isRowEmpty handles all column types correctly.

Let's verify the implementation and usage of isRowEmpty:

✅ Verification successful

The change to use currentRow is correct and consistent with the codebase

The verification reveals that:

  1. isRowEmpty is a utility function defined in packages/nc-gui/utils/dataUtils.ts that takes a record and column as parameters
  2. The change from destructured row to currentRow is correct as currentRow is properly defined as a reactive ref from props
  3. The implementation is consistent with other components in the codebase that use the same pattern (Gallery, Kanban, Calendar views)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for isRowEmpty implementation and its usages
ast-grep --pattern 'function isRowEmpty($_) {
  $$$
}'

# Also check for any related tests
fd -e spec.ts -e spec.js -e test.ts -e test.js | xargs rg 'isRowEmpty'

Length of output: 127


Script:

#!/bin/bash
# Let's try different approaches to find the isRowEmpty implementation

# Search for isRowEmpty method in Vue files
rg -U "isRowEmpty.*?\{.*?\}" -t vue --multiline

# Search for any usage of isRowEmpty
rg "isRowEmpty" -t vue -A 2 -B 2

# Look for the specific component and its methods
rg -U "export default.*?methods.*?\}" -t vue --multiline packages/nc-gui/components/nc/List/RecordItem.vue

Length of output: 275


Script:

#!/bin/bash
# Let's try without file type specification since 'vue' is not recognized

# Search for isRowEmpty method implementation
rg -U "isRowEmpty.*?\{.*?\}" --multiline

# Search for any usage of isRowEmpty with context
rg "isRowEmpty" -A 2 -B 2

# Look at the specific component file
cat packages/nc-gui/components/nc/List/RecordItem.vue

Length of output: 20153

packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (2)

Line range hint 1-450: Implementation looks solid overall

The view locking mechanism is well-implemented with proper error handling, user feedback, and security checks. The code maintains good separation of concerns and follows Vue.js best practices.


Line range hint 78-116: Verify lock type changes across views

The lock type changes might affect other components or views that depend on this state.

✅ Verification successful

Lock type changes are properly handled across the codebase

The lock type changes in ViewActionMenu.vue are well-integrated with other components:

  • The change is reflected in the UI through multiple components (ViewInfo.vue, LockType.vue, LockedViewFooter.vue) that properly react to lock type updates
  • The state is managed consistently through the views store and API updates
  • Proper validations are in place for different lock types (personal, locked, collaborative)
  • The changes are properly persisted through $api.dbView.update
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components that might be affected by lock type changes
rg -l "lock_type" --type vue

# Search for lock type usage patterns
ast-grep --pattern 'view.lock_type = $_'

Length of output: 99


Script:

#!/bin/bash
# Search for lock_type usage in all files
rg -l "lock_type"

# Search for specific lock type related patterns
rg -A 3 "lock_type.*="

# Search for components that might be using lock_type in their template or script
rg -A 3 "lock_type" --type-add 'vue:*.vue' --type vue

Length of output: 17834

packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (2)

2-2: LGTM! Import statement follows TypeScript best practices.

The change to use import type for type-only imports is a good practice as it ensures better tree-shaking and clearer code intentions.


2-2: Verify the impact of removing loadAiIntegrations.

The AI summary indicates that loadAiIntegrations function call was removed. Let's verify if this affects the AI integration functionality.

✅ Verification successful

The removal of loadAiIntegrations is safe

The removal of loadAiIntegrations function appears to be safe. The AI integration functionality is still properly handled through the type system and integration store:

  1. AI integrations are defined in the type system via OpenAI in the integrationType record
  2. The integration type is properly imported through IntegrationCategoryType and SyncDataType from #imports
  3. The component uses the standard integration store hooks for managing integrations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to loadAiIntegrations
rg "loadAiIntegrations" --type vue

# Search for AI integration related code to understand the new flow
ast-grep --pattern 'if ($condition) {
  $$$
  integrationType === "AI"
  $$$
}'

Length of output: 144


Script:

#!/bin/bash
# Search for any remaining references to loadAiIntegrations with proper file extension
rg "loadAiIntegrations"

# Search for AI integration related code to understand the new flow
rg "integrationType.*AI" -A 5 -B 5

Length of output: 883


Script:

#!/bin/bash
# Let's check the file content to understand the changes
cat "packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue"

# Search for OpenAI related integration code
rg "OpenAI" -A 5 -B 5

# Search for AI-related imports
rg "import.*AI" 

Length of output: 97203


Script:

#!/bin/bash
# Let's check for any AI integration related code in the component
rg "AI" packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue

# Let's check for any AI integration related code in the store
rg "AI" packages/nc-gui/composables/useIntegrationsStore.ts

# Let's check for any AI integration related imports in the codebase
rg "import.*AI" packages/nc-gui/components/workspace/integrations/

Length of output: 350

packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (1)

41-43: Verify dynamic integration loading implementation

The addition of loadDynamicIntegrations suggests a shift towards dynamic integration loading. This could impact the initialization flow and state management.

Let's verify the implementation and usage:

✅ Verification successful

Dynamic integration loading implementation verified

The implementation of loadDynamicIntegrations is correctly integrated into the codebase:

  1. It's properly defined in the integration store and used in the IntegrationsTab component
  2. It's called during component mount to initialize dynamic integrations
  3. The function:
    • Loads integrations only once (tracked by integrationsInitialized)
    • Fetches dynamic integrations from the API
    • Sorts them by order
    • Adds them to allIntegrations with proper icon handling
    • Updates the UI via integrationsRefreshKey

The implementation follows a proper initialization flow and state management pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation and other usages of loadDynamicIntegrations
ast-grep --pattern 'loadDynamicIntegrations = $_'

# Search for related integration loading patterns
rg -A 5 'loadDynamicIntegrations'

Length of output: 65767


Script:

#!/bin/bash
# Search for loadDynamicIntegrations implementation and usage
rg -A 5 "loadDynamicIntegrations" --type vue --type ts --type js

# Search for integration store implementation
ast-grep --pattern 'const useIntegrationStore = $_'

Length of output: 65822


Script:

#!/bin/bash
# Search for loadDynamicIntegrations implementation and usage with better file type handling
rg -t js -t ts -t vue "loadDynamicIntegrations"

# Search for integration store definition
rg -t js -t ts -t vue "useIntegrationStore"

# Search for files related to integration store
fd -e ts -e js -e vue "integration" packages/nc-gui

Length of output: 486


Script:

#!/bin/bash
# Let's examine the integration store implementation
cat packages/nc-gui/composables/useIntegrationsStore.ts

# And check the component implementation
cat packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue

Length of output: 35922

packages/nc-gui/components/cell/TextArea.vue (1)

Line range hint 712-721: Check browser compatibility for the :has pseudo-class

The :has CSS pseudo-class is not widely supported in all browsers, notably missing support in Firefox. This may lead to inconsistent styling across different user environments. Consider using alternative selectors or JavaScript solutions to achieve the desired effect.

Refer to browser compatibility information: MDN Web Docs - :has() selector

packages/nc-gui/composables/useIntegrationsStore.ts (5)

191-192: Event emission aligns with analytics tracking

The addition of $e('a:integration:delete') ensures that integration deletion events are properly tracked for analytics purposes.


287-292: Event emissions correctly distinguish between modes

Emi 8000 tting events based on the mode parameter ensures accurate analytics tracking for integration creation and duplication.


198-201: 🛠️ Refactor suggestion

Avoid mutating reactive state directly within array methods

Modifying aiIntegrations.value directly within the filter method may not trigger reactivity. To maintain reactivity, ensure that you are assigning a new array.

Apply this diff to avoid direct mutation:

if (integration.type === IntegrationsType.Ai) {
-  aiIntegrations.value = aiIntegrations.value.filter((i) => i.id !== integration.id)
+  aiIntegrations.value = [...aiIntegrations.value.filter((i) => i.id !== integration.id)]
}

Likely invalid or redundant comment.


93-94: Ensure useNocoAi is imported and aiIntegrations is reactive

The destructuring of { aiIntegrations } from useNocoAi() requires that useNocoAi is properly imported and that aiIntegrations is a reactive reference to ensure reactivity is maintained throughout the application.

Run the following script to verify the import and reactivity:

✅ Verification successful

useNocoAi is properly implemented with reactive aiIntegrations

The implementation is correct:

  • useNocoAi is a shared composable that exports aiIntegrations as a reactive ref
  • aiIntegrations is properly used in useIntegrationsStore.ts with .value access
  • The reactivity chain is maintained throughout the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `useNocoAi` is imported and `aiIntegrations` is reactive.

# Check for the import statement
rg -A1 "import.*useNocoAi" packages/nc-gui/composables/useIntegrationsStore.ts

# Check the declaration of `aiIntegrations` in `useNocoAi`
rg -A5 "function useNocoAi" -g "*.ts" | rg "aiIntegrations\s*=\s*ref|reactive"

Length of output: 161


Script:

#!/bin/bash
# Let's try a different approach to verify the import and reactivity

# First, let's check all imports in the file
echo "=== Checking imports ==="
rg "^import" packages/nc-gui/composables/useIntegrationsStore.ts

# Then, let's find the useNocoAi definition
echo -e "\n=== Finding useNocoAi definition ==="
fd useNocoAi.ts
rg -l "export.*useNocoAi"

# Let's also check the content of useIntegrationsStore.ts around aiIntegrations usage
echo -e "\n=== Checking aiIntegrations context ==="
rg -C 3 "aiIntegrations" packages/nc-gui/composables/useIntegrationsStore.ts

Length of output: 1955


Script:

#!/bin/bash
# Let's check the useNocoAi.ts file to verify the reactivity implementation

echo "=== Checking useNocoAi implementation ==="
cat packages/nc-gui/composables/useNocoAi.ts

Length of output: 9190


541-541: Ensure loadDynamicIntegrations is called appropriately

Now that loadDynamicIntegrations replaces the previous onMounted hook, ensure it is called during component initialization to load dynamic integrations.

Check where loadDynamicIntegrations is invoked to confirm it's integrated properly.

✅ Verification successful

Based on the analysis of the code, I can now generate the final response:

loadDynamicIntegrations is properly called during initialization

The function is correctly called in the onMounted hook of the IntegrationsTab component, ensuring dynamic integrations are loaded when the component is mounted:

onMounted(() => {
  loadDynamicIntegrations()
  // ...
})
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `loadDynamicIntegrations` is called during initialization.

rg -A2 "loadDynamicIntegrations\(\)" packages/nc-gui/**/*.vue

Length of output: 437


Script:

#!/bin/bash
# Let's try a broader search for loadDynamicIntegrations without parentheses
# and also check its usage in .ts/.js files since it might be called from there
rg "loadDynamicIntegrations" packages/nc-gui/

Length of output: 421


Script:

#!/bin/bash
# Let's also check where this function is used through ast-grep
# to catch any method invocations
ast-grep --pattern 'loadDynamicIntegrations($_)'

Length of output: 3408


Script:

#!/bin/bash
# Let's check the file content where loadDynamicIntegrations is defined
# to understand its context better
cat packages/nc-gui/composables/useIntegrationsStore.ts

Length of output: 15350


Script:

#!/bin/bash
# Let's check the IntegrationsTab.vue where loadDynamicIntegrations is called
cat packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue

Length of output: 20571

packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (1)

59-64: 🛠️ Refactor suggestion

Review the timeout implementation for '}' key handler

The setTimeout implementation has several potential issues:

  1. The 250ms delay seems arbitrary and its purpose is not documented
  2. The timeout is not cleared if another key is pressed during the delay, which could lead to race conditions
  3. The special handling of the '}' character is not explained

Consider implementing it this way:

+ // Store timeout reference
+ data() {
+   return {
+     selectedIndex: 0,
+     selectionTimeout: null,
+   }
+ }

  if (event.key === '}') {
+   // Clear any existing timeout
+   if (this.selectionTimeout) {
+     clearTimeout(this.selectionTimeout)
+   }
-   setTimeout(() => {
+   this.selectionTimeout = setTimeout(() => {
      this.selectItem(this.selectedIndex)
+     this.selectionTimeout = null
    }, 250)
  }
✅ Verification successful

Let me try another search pattern to find timeout usage in the codebase.


Let me try one more search to find any similar patterns in the codebase.


Let me try one final search with a different pattern.


Now I have enough context to provide a final response about the timeout implementation.

The timeout implementation for '}' key is intentional and safe

The timeout in the } key handler is part of a field selection component that handles various keyboard interactions for a mention/suggestion list. The 250ms delay appears to be intentional to:

  1. Allow the } character to be processed first before triggering the selection
  2. Avoid race conditions with other key handlers since:
    • Each key handler returns a boolean indicating if it handled the event
    • The } handler is the only one using setTimeout and returns false
    • Other immediate handlers (Tab, Enter, Arrow keys) return true to prevent further processing

The current implementation is safe because:

  • The component maintains a single selectedIndex in its data
  • The timeout only triggers selectItem() with the current selectedIndex
  • No other handlers modify the selection during the timeout period

While the suggested refactoring would add extra safety, it's not essential in this case since there are no observable race conditions in the current implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar timeout patterns in other key handlers
rg -A 5 "setTimeout.*selectItem" 

Length of output: 65571


Script:

#!/bin/bash
# Search for timeout patterns in the codebase
rg -A 5 "setTimeout.*select" packages/nc-gui/

# Also search for any other timeouts in the same file
rg -A 5 "setTimeout" packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue

Length of output: 65667


Script:

#!/bin/bash
# Search for keyboard event handlers in the same file
ast-grep --pattern 'if (event.key === $_) {
  $$$
}'  packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue

Length of output: 117


Script:

#!/bin/bash
# Search for the entire file content to understand the context
cat packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue

Length of output: 2803

packages/nocodb/src/models/AIColumn.ts (1)

5-48: Implementation of AIColumn class is well-structured

The AIColumn class correctly extends LongTextColumn, incorporating AI-specific properties and methods such as fk_integration_id, model, prompt, and prompt_raw. The static methods insert and update are appropriately designed for handling AI columns.

🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (1)

9-113: LongTextColumn abstract class is properly defined

The abstract class LongTextColumn provides a solid foundation for long text columns with necessary properties and methods like _insert, read, and _update. The use of caching and error handling enhances performance and reliability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

6-70: SQL queries are safely parameterized and handle multiple databases

The functions convertAIRecordTypeToValue and convertValueToAIRecordType correctly construct parameterized SQL queries for different database types, mitigating SQL injection risks and ensuring compatibility.

packages/nocodb/src/db/sortV2.ts (1)

202-231: Sorting logic for AI prompt columns is correctly implemented

The addition handles sorting of AI prompt columns by extracting the 'value' field from the JSON data, with appropriate SQL expressions for each supported database client.

packages/nc-gui/utils/columnUtils.ts (1)

269-318: isColumnInvalid function accurately validates AI columns

The updated function effectively checks for AI integrations and properly sets the invalid state and tooltips based on the presence of fk_integration_id and the isReadOnly flag.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (2)

Line range hint 251-269: Handle AIPrompt type correctly in onSelectType

When uidt is AIPrompt, the onSelectType function sets formState.value.uidt to UITypes.LongText and adds meta properties. Ensure that this transformation aligns with the application's logic and that AIPrompt is handled consistently throughout the codebase.


602-604: Use consistent naming for AI prompt detection

The function isAiPromptSelectOption checks if uidt equals UITypes.LongText and isAIPromptCol(formState.value). Ensure that this check is consistent with other parts of the code that handle AI prompts.

packages/nocodb/src/db/conditionV2.ts (1)

596-613: Correctly handling AI prompt columns in query conditions

The code appropriately adjusts the field extraction for AI prompt columns by handling different database clients and extracting the 'value' property from JSON.

packages/nocodb/src/models/Column.ts (6)

452-469: Properly inserting AI options for LongText columns

The code correctly checks for AI prompt metadata and inserts AI column options when the column is of type LongText with AI prompt enabled.


573-577: Fetching AI column options for LongText columns

The code appropriately retrieves AI column options for LongText columns with AI prompt metadata set to true.


1101-1106: Handling deletion of AI LongText column metadata

The code correctly sets the meta table and cache scope names for AI LongText columns during deletion, ensuring associated metadata is properly cleaned up.


1353-1369: Removing AI column options during update

The code appropriately deletes AI column options and clears the related cache when updating a LongText column, maintaining data integrity.


1957-1970: Adding AI options in bulk column insertion

The code properly handles bulk insertion of AI column options for LongText columns with AI prompts, ensuring all necessary properties are included.


2037-2044: Performing bulk insert for AI LongText columns

The code correctly inserts AI column options into the metadata table during a bulk insert operation for LongText columns with AI prompts.

packages/nocodb/src/services/columns.service.ts (2)

71-74: Importing AI data conversion utilities

The import statements correctly include AI data conversion functions needed for handling AI prompt columns.


1562-1630: Handling data conversion for AI prompt columns during update

The code correctly manages data conversion when toggling AI prompt functionality on a LongText column, ensuring that data is converted appropriately based on the column's AI status.

packages/nocodb/src/db/BaseModelSqlv2.ts (1)

13-19: Imports are appropriately added and utilized

The newly imported isAIPromptCol and LongTextAiMetaProp are necessary for handling AI prompt columns and are correctly used in the codebase.

packages/nc-gui/components/general/IntegrationIcon.vue (1)

5-5: size prop is correctly made optional with a default

The size prop is now optional and has a default value of 'sm', improving the component's flexibility and usability.

packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1)

4-27: Migration file correctly defines the new table

The up and down methods properly create and drop the COL_LONG_TEXT table with appropriate columns and indices.

packages/nocodb-sdk/src/lib/index.ts (1)

15-15: Exporting isAIPromptCol enhances SDK functionality

Adding isAIPromptCol to the exports allows other modules to identify AI prompt columns, improving code reusability.

packages/nocodb/src/models/index.ts (1)

51-52: New models LongTextColumn and AIColumn are properly exported

Exporting LongTextColumn and AIColumn makes them available for use throughout the application.

packages/nc-gui/components/smartsheet/expanded-form/Sidebar/Audits.vue (1)

86-91: LGTM! Improved component formatting

The multi-line formatting of GeneralUserIcon improves code readability while maintaining functionality.

packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (1)

129-131: LGTM! Consistent styling for AI components

The styling is consistently applied to both AI-related components using the existing color scheme.

packages/nc-gui/components/cell/AI.vue (3)

16-19: LGTM: Good security practice with role-based access control

The addition of role-based access control and public status checking enhances security for AI feature access.


111-112: LGTM: Proper Vue reactivity handling

Creating a new object reference using spread operator ensures proper reactivity in Vue.


47-47: Consider additional validation for i 67ED ntegration ID

The simplified check return !!fkIntegrationId might miss edge cases where the ID exists but is invalid.

Consider adding validation to ensure the integration ID is valid:

-  return !!fkIntegrationId
+  return !!fkIntegrationId && aiIntegrations.some(integration => integration.id === fkIntegrationId)
packages/nocodb/src/utils/globals.ts (1)

15-15: LGTM! Verify the impact of enum changes.

The reclassification of AI prompt columns as long text columns is consistent across both enums. The versioning suffix is maintained for backward compatibility.

Let's verify the usage of these new enum values and ensure no references to the old ones remain:

Also applies to: 148-148

✅ Verification successful

Let me verify the migration files to ensure the transition from AI to long text columns was handled properly.


Let me check the content of the other AI-related migration file to ensure there are no conflicts.


Enum changes have been properly implemented with no legacy references

Based on the verification:

  • No references to COL_AI exist in the codebase
  • COL_LONG_TEXT is consistently used across models and migrations
  • Migration nc_069_ai_prompt.ts properly creates the table using MetaTable.COL_LONG_TEXT
  • No conflicting schema definitions or migrations were found
  • The AI button migration (nc_066_ai_button.ts) handles a separate concern and doesn't interfere
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to COL_AI
# and verify the adoption of COL_LONG_TEXT

# Check for any remaining COL_AI references
rg "COL_AI"

# Check adoption of new COL_LONG_TEXT
rg "COL_LONG_TEXT"

Length of output: 1531


Script:

#!/bin/bash
# Check migration files for any AI to long text transition
rg -A 10 "nc_069_ai_prompt" packages/nocodb/src/meta/migrations/v2/

Length of output: 67


Script:

#!/bin/bash
# Let's check the migration file directly and any references to column type transitions
cat packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts

# Also check for any other potential migration files related to this change
fd "nc_.*ai.*" packages/nocodb/src/meta/migrations/

Length of output: 963


Script:

#!/bin/bash
# Check the content of the AI button migration file
cat packages/nocodb/src/meta/migrations/v2/nc_066_ai_button.ts

# Also verify there are no direct references to AI columns in the schema
rg -A 5 "createTable.*AI" packages/nocodb/src/meta/migrations/

Length of output: 683

packages/nc-gui/components/ai/PromptWithFields.vue (3)

Line range hint 19-31: LGTM! ReadOnly prop implementation.

The readOnly prop is properly implemented with a default value of false.


74-81: LGTM! Enhanced field tag rendering.

Good implementation of conditional styling for attachment fields and proper setup for tooltips.


185-202: LGTM! Proper disabled state handling.

The button's disabled state and icon opacity are correctly managed based on the readOnly prop.

packages/nc-gui/components/ai/Settings.vue (2)

10-15: LGTM! Proper early return implementation.

The isEditColumn prop is well implemented with proper type safety and early return in the mounted hook.

Also applies to: 24-25, 70-72


215-221: LGTM! Proper scoped styling.

The scoped styling for select components is well implemented with proper deep selector usage.

packages/nc-gui/composables/useNocoAi.ts (1)

20-21: LGTM: Type change improves flexibility

The change to Partial<IntegrationType>[] is a good improvement as it better represents the actual data structure where not all integration properties may be available.

packages/nocodb-sdk/src/lib/UITypes.ts (1)

94-94: LGTM: AIPrompt type addition

The addition of the AIPrompt type to UITypesName is clean and follows the existing pattern.

packages/nc-gui/components/virtual-cell/components/ListItem.vue (1)

162-162: LGTM: Icon component standardization

The change from MaximizeIcon to GeneralIcon with the "maximize" icon maintains the same functionality while standardizing icon usage across components.

packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1)

133-136: LGTM: Added AI button type handling

The addition of AI button type handling in the columnTypeName computed property is well-implemented and follows the existing pattern for special column types.

packages/nc-gui/components/smartsheet/PlainCell.vue (2)

3-3: LGTM: Added AI-related imports

The addition of AIRecordType and isAIPromptCol imports provides proper typing and type checking for AI prompt columns.

Also applies to: 10-10


207-211: LGTM: Added AI prompt column handling

The modification to getTextAreaValue properly handles AI prompt columns by extracting the value property from AIRecordType.

packages/nc-gui/components/smartsheet/Cell.vue (1)

196-196: Improved template structure with consistent cell type handling.

The template changes effectively utilize the new cellType computed property for conditional rendering.

Let's verify that all cell types are properly handled:

Also applies to: 212-263

✅ Verification successful

Let me gather more information about the cell type handling in the component.


Let me try a different approach to verify the cell type handling.


Cell type handling is properly implemented and consistent.

The verification confirms that the cellType computed property in Cell.vue comprehensively handles all UI types from the SDK:

  • All basic types (text, number, boolean) are covered
  • All specialized types (date, time, attachment, etc.) are handled
  • Each cell type has a corresponding lazy-loaded component
  • The conditional rendering in the template matches the computed cell types
  • The AI cell type is properly integrated with the existing cell type system
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all UITypes have corresponding cell components
# and that no cell types are missing in the cellType computed property

# Search for all UITypes definitions
rg "UITypes\." packages/nocodb-sdk/src/lib/UITypes.ts

# Search for all cell components
fd "Cell.*\.vue" packages/nc-gui/components/cell/

Length of output: 4843


Script:

#!/bin/bash
# Check the cellType computed property implementation and cell type handling in Cell.vue
ast-grep --pattern 'computed: {
  $$$
  cellType() {
    $$$
  }
  $$$
}'

# Also check for conditional rendering based on cellType
rg -A 2 'v-if.*cellType' packages/nc-gui/components/smartsheet/Cell.vue

Length of output: 149


Script:

#!/bin/bash
# Search for the content of Cell.vue to analyze cellType handling
cat packages/nc-gui/components/smartsheet/Cell.vue

# Also check for any other cell type related components
fd ".*Cell.*\.vue" packages/nc-gui/components/smartsheet/

Length of output: 11430

packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (1)

58-58: Migration properly integrated following established patterns.

The AI prompt migration is correctly added following the existing migration structure and naming conventions.

Let's verify the migration sequence and implementation:

Also applies to: 125-125, 249-250

packages/nc-gui/composables/useColumnCreateStore.ts (1)

3-3: Enhanced AI mode detection with proper type checking.

The isAiMode computed property now correctly handles AI prompt columns using the imported utility function.

Also applies to: 113-116

packages/nc-gui/components/cell/attachment/index.vue (1)

463-463: LGTM: Icon update for consistency

The icon change from expand to maximize aligns with similar updates in other components.

packages/nc-gui/helpers/parsers/parserHelpers.ts (1)

481-484: LGTM: AI-specific column naming logic

The changes add proper handling for AI button and AI prompt column types, following the existing patterns and maintaining code consistency.

Also applies to: 486-490

packages/nocodb/src/models/Integration.ts (2)

454-457: LGTM: Defensive initialization of config.models

Good addition of null check and array initialization to prevent potential runtime errors.


474-500: Verify the cleanup of integration associations

The code adds cleanup of button and long text associations during integration deletion. Let's verify this handles all cases correctly.

Also applies to: 522-547

✅ Verification successful

Based on the search results, I can now provide a final response:

Integration cleanup implementation is complete and correct

The code correctly handles the cleanup of integration associations by:

  1. Nullifying fk_integration_id and model fields in both button and long text columns
  2. Using proper workspace scoping with this.fk_workspace_id ? this.fk_workspace_id : RootScopes.WORKSPACE
  3. Ensuring atomic updates through ncMeta.metaUpdate with proper where conditions

The implementation is thorough and handles all known integration references in the schema, including:

  • Button columns (MetaTable.COL_BUTTON)
  • Long text columns (MetaTable.COL_LONG_TEXT)
  • AI columns (MetaTable.COL_AI)
  • Sources (MetaTable.SOURCES)
  • Integration store (MetaTable.INTEGRATION_STORE)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential orphaned references after integration deletion

# Check for any remaining references to deleted integrations in buttons
ast-grep --pattern 'fk_integration_id = $_' 

# Check for any remaining references to deleted integrations in long texts
rg -A 5 'fk_integration_id'

Length of output: 65755


Script:

#!/bin/bash
# Let's search for integration references in the codebase
rg -A 5 "fk_integration_id" --type ts

# Also check for any other tables that might reference integrations
ast-grep --pattern 'MetaTable.$_ = $_' --lang typescript

Length of output: 26508

packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2)

4-9: LGTM! Import organization looks good.

The imports are well-organized and properly grouped, with the new isAIPromptCol import added alongside related functions.


443-443: Verify AI prompt column handling in other duplication scenarios.

The condition !isVirtualCol(destColumn) && !isAIPromptCol(destColumn) ensures that AI prompt columns are handled correctly during duplication. However, we should verify that AI prompt columns are properly handled in other duplication scenarios.

✅ Verification successful

Let me analyze the findings and generate additional verification scripts to check the implementation details of AI prompt column handling in different scenarios.


Based on the analysis of the code, I can now generate the final response:

AI prompt columns are properly handled across duplication and import scenarios

The codebase shows consistent handling of AI prompt columns:

  • In duplication: AI prompt columns are excluded from column updates along with virtual columns (!isVirtualCol(destColumn) && !isAIPromptCol(destColumn))
  • In import: AI prompt columns have special workspace and integration handling
  • The implementation is consistent across the export-import module
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other places where AI prompt columns might need special handling

# Test: Search for other column duplication or handling logic
ast-grep --pattern 'if ($cond) {
  $$$
  isVirtualCol($col)
  $$$
}'

# Test: Search for AI prompt column handling in other files
rg "isAIPromptCol"

Length of output: 3619


Script:

#!/bin/bash
# Check implementation details of AI prompt column handling in import and duplication scenarios
ast-grep --pattern 'if ($cond) {
  $$$
  isAIPromptCol($col)
  $$$
}'

# Check for column type validation and processing
rg -A 5 "isAIPromptCol\(.*\)" packages/nocodb/src/modules/jobs/jobs/export-import/

Length of output: 2723

packages/nc-gui/components/smartsheet/Gallery.vue (1)

376-376: LGTM! Icon change improves UI consistency.

Changed from expand to maximize icon for better visual consistency with other expand/maximize actions across the application.

packages/nc-gui/components/smartsheet/expanded-form/index.vue (1)

33 A3DB 0-333: LGTM: Improved user feedback for URL copy action

The addition of a 5-second delay provides better user feedback after copying the URL, giving users sufficient time to see the confirmation state.

packages/nc-gui/composables/useData.ts (2)

Line range hint 146-160: Enhanced column options handling for export

Good improvements in handling column options:

  1. Added support for fk_model_id mapping
  2. Preserved workspace and integration values during export
  3. Added proper handling of output_column_ids by mapping through idMap

Line range hint 180-181: Improved special column type handling

Good additions for handling special column types:

  1. Skip formula processing for button columns
  2. Added support for AI prompt columns in updates

Also applies to: 289-289

packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (1)

628-636: LGTM: Added proper handling of AI prompt columns

Good addition of AI prompt column handling during export. The code properly attempts to serialize the value as JSON while gracefully falling back to the original value if serialization fails.

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (4)

182-182: LGTM: Added AI prompt column filtering

The addition of !isAIPromptCol(a) to the filter condition is correct, ensuring AI prompt columns are excluded from the initial table creation phase, as they require special handling.


1113-1114: LGTM: Added AI prompt column check in referenced columns

The addition of isAIPromptCol(a) to the referenced columns check is correct, ensuring AI prompt columns are properly handled during the import process.


1281-1287: LGTM: Added workspace validation for AI prompt columns

The workspace validation ensures that AI prompt columns are properly scoped to their workspace, with appropriate nulling of workspace and integration IDs when mismatched.


Line range hint 1288-1300: LGTM: Added AI prompt column creation logic

The implementation correctly handles the creation of AI prompt columns with their associated properties (integration ID, model, prompt raw).

packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (2)

997-998: LGTM: Enhanced AI column type checking

The condition now correctly handles both AI prompt columns and AI buttons, improving the accuracy of AI-related functionality detection.


1021-1022: LGTM: Added AI button output column handling

The implementation correctly extracts output column IDs for AI buttons, ensuring proper handling of AI-generated content.

packages/nc-gui/components/smartsheet/grid/Table.vue (2)

855-856: LGTM: Enhanced AI column type checking

The condition now correctly handles both AI prompt columns and AI buttons, maintaining consistency with the InfiniteTable implementation.


Line range hint 879-884: LGTM: Added AI button output column handling

The implementation correctly extracts output column IDs for AI buttons, maintaining consistency with the InfiniteTable implementation.

packages/nc-gui/composables/useMultiSelect/index.ts (1)

6-6: LGTM: Type import is correctly placed.

The AIRecordType import is appropriately grouped with other types from the 'nocodb-sdk' package.

Comment on lines 629 to 648
<GeneralIcon icon="refresh" :class="{ 'animate-infinite animate-spin': isAiGenerating }" />
<span class="text-sm font-bold"> {{ isAiGenerating ? 'Re-generating...' : 'Re-generate' }} </span>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the usage of Tailwind CSS classes

The class animate-infinite is not a valid Tailwind CSS utility. The animate-spin class already provides an infinite spin animation. Remove animate-infinite to prevent any unexpected behavior.

Apply this diff to correct the class:

<GeneralIcon icon="refresh" :class="{
-  'animate-infinite animate-spin': isAiGenerating
+  'animate-spin': isAiGenerating
}" />
📝 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.

Suggested change
<GeneralIcon icon="refresh" :class="{ 'animate-infinite animate-spin': isAiGenerating }" />
<span class="text-sm font-bold"> {{ isAiGenerating ? 'Re-generating...' : 'Re-generate' }} </span>
<GeneralIcon icon="refresh" :class="{ 'animate-spin': isAiGenerating }" />
<span class="text-sm font-bold"> {{ isAiGenerating ? 'Re-generating...' : 'Re-generate' }} </span>

Comment on lines 10071 to 10076
value?: string;
lastModifiedBy?: string;
lastModifiedTime?: string;
isStale?: string;
} = {};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the type of isStale to boolean

The isStale property is used as a boolean in the code. Currently, it's typed as string?, which can lead to type mismatches. It should be typed as boolean? to reflect its usage.

Apply this diff to fix the type:

const obj: {
  value?: string;
  lastModifiedBy?: string;
  lastModifiedTime?: string;
- isStale?: string;
+ isStale?: boolean;
} = {};
📝 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.

Suggested change
value?: string;
lastModifiedBy?: string;
lastModifiedTime?: string;
isStale?: string;
} = {};
value?: string;
lastModifiedBy?: string;
lastModifiedTime?: string;
isStale?: boolean;
} = {};

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch from 8c6e440 to 0a49d91 Compare December 5, 2024 17:28
Copy link
Contributor
@coderabbitai coderabbitai bot left a 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

🧹 Outside diff range and nitpick comments (20)
packages/nc-gui/utils/commonUtils.ts (1)

91-93: Consider memoization for performance optimization

If this function is used frequently with the same inputs (e.g., in tight loops or frequent re-renders), consider memoizing the results to avoid redundant string operations.

+const searchCache = new Map<string, Map<string, boolean>>();
+
 export const ncSearchCompare = (source?: string, query?: string): boolean => {
-  return (source || '').toLowerCase().includes((query || '').toLowerCase())
+  const normalizedSource = source || '';
+  const normalizedQuery = query || '';
+  
+  // Check cache
+  const queryCache = searchCache.get(normalizedSource);
+  if (queryCache?.has(normalizedQuery)) {
+    return queryCache.get(normalizedQuery)!;
+  }
+  
+  // Compute result
+  const result = normalizedSource.toLowerCase().includes(normalizedQuery.toLowerCase());
+  
+  // Cache result
+  if (!searchCache.has(normalizedSource)) {
+    searchCache.set(normalizedSource, new Map());
+  }
+  searchCache.get(normalizedSource)!.set(normalizedQuery, result);
+  
+  // Prevent unbounded cache growth
+  if (searchCache.size > 1000) {
+    const firstKey = searchCache.keys().next().value;
+    searchCache.delete(firstKey);
+  }
+  
+  return result;
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: Consider optimizing the copy confirmation UX

The 5-second delay for the copy confirmation might be too long and could confuse users. Additionally, error handling should be added for the copy operation.

Consider applying these improvements:

 const  () => {
-  await copy(view.value!.id!)
-  isViewIdCopied.value = true
-
-  await ncDelay(5000)
-
-  isViewIdCopied.value = false
+  try {
+    await copy(view.value!.id!)
+    isViewIdCopied.value = true
+    await ncDelay(2000) // Reduce to 2 seconds for better UX
+    isViewIdCopied.value = false
+  } catch (e: any) {
+    message.error(t('msg.error.copyToClipboardError'))
+  }
 }
packages/nc-gui/components/smartsheet/calendar/MonthView.vue (5)

Line range hint 236-583: Consider refactoring recordsToDisplay for better maintainability

The computed property is quite complex and handles multiple responsibilities. Consider breaking it down into smaller, more focused functions:

  1. Lane calculation logic
  2. Single-day event handling
  3. Multi-day event handling

Example refactor for lane calculation:

+ const calculateLaneForDate = (dateKey: string, duration: number = 1, lanes: boolean[], maxLanes: number): number => {
+   for (let i = 0; i < maxLanes; i++) {
+     if (!lanes[i]) {
+       let isAvailable = true
+       for (let j = 0; j < duration; j++) {
+         const checkDate = dayjs(dateKey).add(j, 'day').format('YYYY-MM-DD')
+         if (recordsInDay[checkDate]?.lanes[i]) {
+           isAvailable = false
+           break
+         }
+       }
+       if (isAvailable) return i
+     }
+   }
+   return -1
+ }

Line range hint 752-752: Address TODO comment for drag element selection

The TODO comment indicates a potential improvement in drag element selection logic.

Would you like me to help implement the drag element selection logic or create a GitHub issue to track this task?


Line range hint 584-751: Add error boundaries for drag event handling

The drag event handlers could benefit from try-catch blocks to gracefully handle edge cases and prevent potential crashes.

Example implementation:

 const  MouseEvent) => {
   if (!isUIAllowed('dataEdit') || !dragRecord.value) return
 
+  try {
     calculateNewRow(event, false)
+  } catch (error) {
+    console.error('Error during drag operation:', error)
+    stopDrag(event)
+  }
 }

Line range hint 933-940: Optimize template rendering for fields loop

The v-for loop for fields could impact performance with large datasets. Consider using virtual scrolling or pagination for better performance.

Consider using a virtual scroller component:

+ import { VirtualScroller } from '@vueuse/components'
+
- <template v-for="field in fields" :key="field.id">
+ <VirtualScroller
+   :items="fields"
+   :item-height="20"
+ >
+   <template #item="{ item: field }">
     <LazySmartsheetPlainCell
       v-if="!isRowEmpty(record, field!)"
       v-model="record.row[field!.title!]"
       class="text-xs"
       :bold="fieldStyles[field.id].bold"
       :column="field"
       :italic="fieldStyles[field.id].italic"
       :underline="fieldStyles[field.id].underline"
     />
+   </template>
+ </VirtualScroller>

Line range hint 841-932: Enhance accessibility for interactive calendar elements

The calendar grid and interactive elements could benefit from additional ARIA attributes for better accessibility.

Add necessary ARIA attributes:

 <div
   ref="calendarGridContainer"
+  role="grid"
+  aria-label="Calendar Month View"
   :class="{
     'grid-rows-5': calendarData.weeks.length === 5,
     'grid-rows-6': calendarData.weeks.length === 6,
     'grid-rows-7': calendarData.weeks.length === 7,
   }"
   class="grid"
   style="height: calc(100% - 1.59rem)"
   @drop="dropEvent"
 >
   <div
     v-for="week in calendarData.weeks"
     :key="week.weekIndex"
+    role="row"
     :class="calendarData.gridClass"
     data-testid="nc-calendar-month-week"
   >
packages/nc-gui/components/workspace/project/AiCreateProject.vue (1)

Line range hint 164-199: Improve timer cleanup to prevent memory leaks

While the error handling and state management are solid, the timer cleanup could be improved to prevent potential memory leaks in edge cases.

Consider adding cleanup in component unmount:

+ onBeforeUnmount(() => {
+   if (timerId) {
+     clearInterval(timerId)
+     timerId = undefined
+   }
+ })

Also, consider adding a safety check before setting new intervals:

-    timerId = setInterval(displayCharByChar, 40)
+    if (timerId) {
+      clearInterval(timerId)
+    }
+    timerId = setInterval(displayCharByChar, 40)
packages/nc-gui/components/cell/AI.vue (1)

81-92: Consider removing the arbitrary timeout.

The 100ms timeout for resetting isAiEdited seems arbitrary and could be replaced with a more deterministic approach, such as using Vue's nextTick or handling the state change in the proper lifecycle hook.

-        setTimeout(() => {
-          isAiEdited.value = false
-        }, 100)
+        nextTick(() => {
+          isAiEdited.value = false
+        })
packages/nc-gui/components/ai/PromptWithFields.vue (3)

Line range hint 19-31: Add TypeScript documentation for the readOnly prop.

Consider adding JSDoc documentation to describe the purpose and usage of the readOnly prop.

  defineProps<{
    modelValue: string
    options?: ColumnType[]
    autoFocus?: boolean
    promptFieldTagClassName?: string
    suggestionIconClassName?: string
    placeholder?: string
+   /** When true, the prompt editor becomes non-editable */
    readOnly?: boolean
  }>()

63-68: Optimize the filter implementation.

The current implementation makes multiple calls to toLowerCase() and uses nested optional chaining. Consider optimizing it for better performance.

- return (
-   props.options?.filter(
-     (o) =>
-       o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
-   ) ?? []
- )
+ const lowercaseQuery = query.toLowerCase()
+ return props.options?.filter((o) => {
+   const title = o.title?.toLowerCase()
+   return title ? (title.includes(lowercaseQuery) || `${title}}` === lowercaseQuery) : false
+ }) ?? []

185-202: Enhance button accessibility and UX.

The button's disabled state could be more accessible and user-friendly.

  <NcButton
    size="xs"
    type="text"
    class="nc-prompt-with-field-suggestion-btn !px-1"
+   :class="{ 'cursor-not-allowed': readOnly }"
+   :aria-label="readOnly ? 'Field suggestion disabled in read-only mode' : 'Add field suggestion'"
    :disabled="readOnly"
    @click.stop="newFieldSuggestionNode"
  >
packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: Avoid using 'this' in static methods

Using this inside static methods can be confusing because this refers to the class itself, not an instance. Consider using the class name LongTextColumn instead for clarity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: Avoid using 'this' in static methods

Using this inside static methods can be confusing because this refers to the class itself, not an instance. Consider using the class name LongTextColumn instead for clarity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/AIColumn.ts (2)

26-26: Avoid using 'this' in static methods

Using this inside static methods can be confusing because it refers to the class rather than an instance. Replace this with the class name AIColumn for better clarity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: Avoid using 'this' in static methods

Using this inside static methods can be confusing because it refers to the class rather than an instance. Replace this with the class name AIColumn for better clarity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nc-gui/utils/columnUtils.ts (1)

290-296: Refactor duplicated logic in 'isColumnInvalid' function

The logic for determining the invalid state of AI-related columns in both the Button and LongText cases is duplicated. Consider extracting this logic into a helper function to improve maintainability and reduce code duplication.

Also applies to: 303-309

packages/nocodb/src/services/columns.service.ts (2)

1562-1629: LGTM! Consider enhancing error messages for better debugging.

The AI prompt column handling implementation is well-structured and handles both conversion scenarios correctly. The column alias substitution is properly implemented with error checking.

Consider enhancing the error message in the field not found check to include more context:

-  NcError.badRequest(`Field '${p1}' not found`);
+  NcError.badRequest(`Field '${p1}' not found in table '${table.title}'`);

2266-2288: Consider extracting common prompt processing logic.

The prompt processing logic is duplicated between columnAdd and columnUpdate methods. Consider extracting it into a shared utility function for better maintainability.

Example refactor:

+ private async processAIPrompt(context: NcContext, table: Model, promptRaw: string): Promise<string> {
+   if (!promptRaw) return '';
+   
+   await table.getColumns(context);
+   
+   return promptRaw.replace(/{(.*?)}/g, (match, p1) => {
+     const column = table.columns.find((c) => c.title === p1);
+     
+     if (!column) {
+       NcError.badRequest(`Field '${p1}' not found in table '${table.title}'`);
+     }
+     
+     return `{${column.id}}`;
+   });
+ }

// In columnUpdate and columnAdd:
- prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
-   const column = table.columns.find((c) => c.title === p1);
-   if (!column) {
-     NcError.badRequest(`Field '${p1}' not found`);
-   }
-   return `{${column.id}}`;
- });
+ prompt = await this.processAIPrompt(context, table, colBody.prompt_raw);
packages/nocodb/src/db/BaseModelSqlv2.ts (1)

8635-8653: Add error handling for non-array JSON values

The method _convertJsonType assumes that d[col.id] is either a string or an array. If it's an object, the code may not handle it correctly.

if (d[col.id] && typeof d[col.id] === 'string') {
  try {
    d[col.id] = JSON.parse(d[col.id]);
  } catch {}
}
+ else if (d[col.id] && typeof d[col.id] === 'object' && !Array.isArray(d[col.id])) {
+   // Handle object case if necessary
+   // Possibly validate or process the object
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8c6e440 and 0a49d91.

📒 Files selected for processing (72)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/dashboard/settings/base/index.vue (0 hunks)
  • packages/nc-gui/components/feed/View.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/calendar/MonthView.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (5 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (5 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nc-gui/components/dashboard/settings/base/index.vue
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (41)
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/models/index.ts
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/components/ai/Settings.vue
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/components/feed/View.vue
  • packages/nocodb/src/db/sortV2.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nocodb/src/models/Integration.ts
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nc-gui/components/general/Loader.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
👮 Files not reviewed due to content moderation or server errors (8)
  • packages/nc-gui/composables/useNocoAi.ts
  • packages/nc-gui/composables/useIntegrationsStore.ts
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nc-gui/components/smartsheet/details/Fields.vue
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  • packages/nc-gui/components/smartsheet/grid/Table.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (38)
packages/nc-gui/utils/commonUtils.ts (1)

74-93: Implementation looks good!

The function is well-documented with comprehensive examples and handles edge cases appropriately. Note that there's already an existing review comment about adding test coverage.

packages/nc-gui/components/cell/TextArea.vue (6)

Line range hint 408-421: LGTM! Well-structured warning UI

The warning UI for stale AI content is well-implemented with clear messaging and proper styling.


526-527: Remove redundant animation class

The animate-infinite class is redundant as animate-spin already provides an infinite animation.


629-630: Remove redundant animation class

The animate-infinite class is redundant as animate-spin already provides an infinite animation.


Line range hint 637-648: LGTM! Clear and well-structured warning modal

The warning modal implementation follows good UI practices with clear messaging and proper visual hierarchy.


Line range hint 708-724: LGTM! Well-organized styles

The styles for AI-specific cases are well-organized and follow the project's conventions.


100-103: ⚠️ Potential issue

Remove non-null assertions to prevent potential runtime errors

The use of non-null assertions (!) in meta.value!.columns! may lead to runtime errors if meta.value or columns are undefined.

Apply this diff to handle possible null values:

const rowId = computed(() => {
-  return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
+  if (!meta.value?.columns || !currentRow.value?.row) {
+    return null
+  }
+  return extractPkFromRow(currentRow.value.row, meta.value.columns)
})

Likely invalid or redundant comment.

packages/nc-gui/components/workspace/project/AiCreateProject.vue (1)

31-32: LGTM: Event tracking implementation

The event emitter is properly integrated to track AI base generation events with the constructed prompt as payload.

Also applies to: 163-163

packages/nc-gui/components/cell/AI.vue (6)

16-18: LGTM: Clean implementation of permission management.

The addition of role-based permissions and public status checking enhances the component's security model.


63-63: LGTM: Good defensive programming.

The addition of filter(Boolean) prevents potential issues with undefined values in the output columns array.


111-112: LGTM: Proper handling of reactivity.

Creating a new object with the spread operator ensures proper reactivity in Vue's change detection system.


118-120: LGTM: Well-structured button state management.

The new computed property effectively consolidates multiple conditions for button state management, improving code maintainability.


Line range hint 160-187: LGTM: Well-structured styling with accessibility considerations.

The styling changes follow Tailwind conventions and properly handle focus states for accessibility.


47-47: Verify the simplified AI integration check.

The change from checking array inclusion to a simple truthiness check of fkIntegrationId might skip validation of whether the integration actually exists in aiIntegrations.

✅ Verification successful

Validation of AI integration ID is handled elsewhere in the codebase

The simplified check in AI.vue is correct as the validation of fkIntegrationId against aiIntegrations is properly handled in other parts of the codebase:

  • columnUtils.ts validates integration existence using ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')
  • useNocoAi.ts composable provides isAiIntegrationAvailableInList function for validation
  • Settings.vue ensures valid integration ID assignment from available integrations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any validation of fk_integration_id against aiIntegrations
rg -A 5 "fk_integration_id.*aiIntegrations" --type vue

Length of output: 84


Script:

#!/bin/bash
# Search for any validation of fk_integration_id in the codebase
rg -A 5 "fkIntegrationId.*aiIntegrations"

# Also search for the pattern without camelCase
rg -A 5 "fk_integration_id.*aiIntegrations"

# Search for any usage of aiIntegrations array
rg -A 5 "aiIntegrations.*\["

Length of output: 2451

packages/nc-gui/components/ai/PromptWithFields.vue (1)

213-213: Consider improving button positioning for long content.

The absolute positioning of the suggestion button might cause overlap issues with long content. Consider using a flex container with proper spacing instead.

Also applies to: 226-228

packages/nocodb/src/utils/dataConversion.ts (1)

1-150: Implementation looks good

The data conversion functions for handling AI record types across different database sources are well-implemented and correctly handle various SQL dialects.

packages/nc-gui/components/smartsheet/Cell.vue (1)

150-176: Well-structured 'cellType' computed property

The introduction of the cellType computed property simplifies the template rendering logic and enhances maintainability by centralizing the cell type determination.

packages/nc-gui/components/smartsheet/expanded-form/index.vue (1)

330-333: LGTM: Added delay for better UX

The addition of a 5-second delay before resetting isRecordLinkCopied improves user feedback after copying the URL.

packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2)

5-5: LGTM: Added import for AI metadata property

The import of LongTextAiMetaProp supports the new AI-enabled LongText field functionality.


832-872: LGTM: Added database-specific handling for AI-enabled LongText fields

The implementation correctly handles AI-enabled LongText fields across different database types:

  • PostgreSQL: Uses TRIM('"' FROM (??::jsonb->>'value'))
  • MySQL: Uses JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))
  • SQLite: Uses json_extract(??, '$.value')
  • MSSQL: Uses JSON_VALUE(??, '$.value')

The code gracefully falls back to returning the column name directly when the field is not AI-enabled.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (5)

186-188: LGTM: Added condition to hide AI prompt option in edit mode

The hideAiPromptOnEdit condition correctly prevents the selection of AI prompt type when editing existing columns.


Line range hint 251-269: LGTM: Enhanced onSelectType to handle AI prompt columns

The function now correctly handles AI prompt columns by:

  1. Setting UIType to LongText
  2. Configuring the appropriate metadata with LongTextAiMetaProp

602-604: LGTM: Added helper function for AI prompt detection

The isAiPromptSelectOption function provides a clean way to detect AI prompt columns in the UI.


1072-1078: LGTM: Updated icon selection logic for AI-related columns

The icon selection logic now properly handles both AI button and AI prompt column types, improving the UI consistency.


1139-1143: LGTM: Added LongText options component with navigation support

The component now includes navigation to integrations for AI-related functionality.

packages/nocodb/src/db/conditionV2.ts (1)

596-612: LGTM! Database-specific JSON value extraction for AI prompts

The implementation correctly handles JSON value extraction for AI prompt columns across different database types:

  • PostgreSQL: Uses jsonb->>'value' with TRIM for proper JSON text extraction
  • MySQL: Uses JSON_UNQUOTE(JSON_EXTRACT()) for proper JSON string handling
  • SQLite: Uses json_extract() for basic JSON path extraction
  • MSSQL: Uses JSON_VALUE() for direct value access
packages/nocodb/src/models/Column.ts (5)

452-469: LGTM! AI column insertion for LongText columns

The implementation correctly handles AI column insertion when the column is marked as an AI prompt through the LongTextAiMetaProp metadata property.


573-577: LGTM! AI column options retrieval

Proper handling of AI column options retrieval for LongText columns marked as AI prompts.


Line range hint 1244-1295: LGTM! Workspace-aware integration handling

The implementation correctly handles workspace and integration IDs for both button and AI prompt columns:

  • Clears workspace-specific data when importing across different workspaces
  • Maintains data integrity by nullifying integration-specific fields

1957-1970: LGTM! Bulk AI column insertion

The implementation correctly handles bulk insertion of AI columns with proper metadata and integration handling.


886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect. It should check for the actual column ID to detect whether the prompt includes the deleted column ID.

Apply this diff to fix the placeholder matching:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (3)

182-182: LGTM! Filter out AI prompt columns during table creation

Correctly excludes AI prompt columns from initial table creation as they require special handling.


1113-1114: LGTM! Include AI prompt columns in referenced column set

Properly includes AI prompt columns in the referenced column set for processing after table creation.


1281-1295: LGTM! Workspace-aware AI prompt column import

The implementation correctly:

  • Handles workspace-specific data during import
  • Clears integration settings when importing across workspaces
  • Preserves column metadata and prompt configuration
packages/nocodb/src/db/BaseModelSqlv2.ts (3)

5721-5723: 🛠️ Refactor suggestion

Consider using Promise.all with insertDatas.map

The current implementation may not properly handle asynchronous operations in the map function. Consider using Promise.all to properly await all async operations.

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.


10062-10104: Verify AI prompt column handling

The new AI prompt column handling logic looks good but should be verified for edge cases:

  1. Ensure proper handling when cookie.system is undefined
  2. Verify behavior when value is null/undefined
✅ Verification successful

Based on the code analysis, I can now provide a definitive response about the AI prompt column handling:

AI prompt column handling is properly implemented with null safety

The implementation in BaseModelSqlv2.ts handles both edge cases correctly:

  1. For cookie.system being undefined:
  • The code uses optional chaining with cookie?.system
  • When undefined, it falls into the else block which has proper null-safe handling using optional chaining for all properties (cookie?.user?.id)
  1. For value being null/undefined:
  • The check if (data[column.column_name]) ensures the code block only executes when there's a value
  • When the value is null/undefined, the code skips the block entirely, preserving the null value
  • When value is an object, it safely extracts the value property with value = value.value

The implementation follows a defensive programming approach with proper null checks and optional chaining throughout the code path.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for AI prompt column usage to verify edge cases
ast-grep --pattern $'isAIPromptCol($_) && $_.column_name'

Length of output: 58


Script:

#!/bin/bash
# Search for isAIPromptCol function definition and usage
ast-grep --pattern 'function isAIPromptCol'

# Search for AI prompt column related code
rg "isAIPromptCol" -A 5 -B 5

Length of output: 29979


10107-10137: Verify isStale flag handling for AI columns

The isStale flag handling for AI columns looks good but has a potential issue:

  1. The referencedColumnIds extraction using regex may miss some edge cases
  2. Consider validating the column IDs before using them
✅ Verification successful

Based on the gathered information, I can now provide the final response:

The regex pattern for AI column references is properly implemented and validated

The implementation is safe and handles edge cases appropriately:

  • The pattern /{(.*?)}/g is used consistently across the codebase
  • Column IDs are validated before use:
    • In columns.service.ts, there's explicit validation that checks if referenced columns exist
    • Error handling is in place with NcError.badRequest when column is not found
  • The prompt_raw field is properly processed with column title to ID conversion
  • The implementation includes proper error storage and handling through the error field in AIColumn model
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for AI column references to verify pattern matching
rg -A 5 "colOptions.prompt?.match"

Length of output: 36


Script:

#!/bin/bash
# Search for AI column prompt pattern matching with broader context
rg -A 10 "prompt\?\.match"

# Search for AI column related code
ast-grep --pattern 'const referencedColumnIds = $_?.match(/{(.*?)}/g)'

# Look for other AI column related patterns
rg -A 10 "isAIPromptCol"

Length of output: 32793


Script:

#!/bin/bash
# Search for prompt pattern matching implementations
ast-grep --pattern 'prompt = $_?.replace(/{(.*?)}/g, $_)'

# Look for other prompt pattern matching implementations
rg -A 5 "prompt.*replace.*{.*?}"

# Search for prompt validation or processing
rg -A 5 "prompt_raw"

Length of output: 9907

packages/nc-gui/components/smartsheet/Kanban.vue (1)

1146-1146: Icon update enhances consistency

The use of iconMap.maximize for the expand record action aligns with the application's iconography updates, ensuring a consistent user experience.

Comment on lines 150 to 161
let prompt = `${aiFormState.value.prompt}`

// Append optional information if provided
if (aiFormState.value.organization?.trim()) {
prompt += ` | Organization: ${aiFormState.value.organization}`
}
if (aiFormState.value.industry?.trim()) {
prompt += ` | Industry: ${aiFormState.value.industry}`
}
if (aiFormState.value.audience?.trim()) {
prompt += ` | Audience: ${aiFormState.value.audience}`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add input sanitization for prompt construction

While the prompt construction logic handles optional fields correctly, it's recommended to sanitize user inputs to prevent potential prompt injection attacks or unexpected behavior.

Consider adding sanitization:

-  let prompt = `${aiFormState.value.prompt}`
+  const sanitizeInput = (input) => input.replace(/[|]/g, '').trim()
+  let prompt = sanitizeInput(aiFormState.value.prompt)

   if (aiFormState.value.organization?.trim()) {
-    prompt += ` | Organization: ${aiFormState.value.organization}`
+    prompt += ` | Organization: ${sanitizeInput(aiFormState.value.organization)}`
   }
   if (aiFormState.value.industry?.trim()) {
-    prompt += ` | Industry: ${aiFormState.value.industry}`
+    prompt += ` | Industry: ${sanitizeInput(aiFormState.value.industry)}`
   }
   if (aiFormState.value.audience?.trim()) {
-    prompt += ` | Audience: ${aiFormState.value.audience}`
+    prompt += ` | Audience: ${sanitizeInput(aiFormState.value.audience)}`
   }
📝 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.

Suggested change
let prompt = `${aiFormState.value.prompt}`
// Append optional information if provided
if (aiFormState.value.organization?.trim()) {
prompt += ` | Organization: ${aiFormState.value.organization}`
}
if (aiFormState.value.industry?.trim()) {
prompt += ` | Industry: ${aiFormState.value.industry}`
}
if (aiFormState.value.audience?.trim()) {
prompt += ` | Audience: ${aiFormState.value.audience}`
}
const sanitizeInput = (input) => input.replace(/[|]/g, '').trim()
let prompt = sanitizeInput(aiFormState.value.prompt)
// Append optional information if provided
if (aiFormState.value.organization?.trim()) {
prompt += ` | Organization: ${sanitizeInput(aiFormState.value.organization)}`
}
if (aiFormState.value.industry?.trim()) {
prompt += ` | Industry: ${sanitizeInput(aiFormState.value.industry)}`
}
if (aiFormState.value.audience?.trim()) {
prompt += ` | Audience: ${sanitizeInput(aiFormState.value.audience)}`
}

Comment on lines 159 to 178
function loadMentionFieldTagTooltip() {
document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
if (!tooltip || el.scrollWidth <= el.clientWidth) return
// Show tooltip only on truncate
tippy(el, {
content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
placement: 'top',
allowHTML: true,
arrow: true,
animation: 'fade',
duration: 0,
maxWidth: '200px',
})
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add tooltip cleanup on component unmount.

The tooltips are properly initialized, but they should be cleaned up when the component is unmounted to prevent memory leaks.

+ const tooltipInstances: any[] = []

  function loadMentionFieldTagTooltip() {
    document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
      const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
      if (!tooltip || el.scrollWidth <= el.clientWidth) return
      // Show tooltip only on truncate
-     tippy(el, {
+     const instance = tippy(el, {
        content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
        placement: 'top',
        allowHTML: true,
        arrow: true,
        animation: 'fade',
        duration: 0,
        maxWidth: '200px',
      })
+     tooltipInstances.push(instance)
    })
  }

+ onBeforeUnmount(() => {
+   tooltipInstances.forEach(instance => instance.destroy())
+   tooltipInstances.length = 0
+ })
📝 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.

Suggested change
function loadMentionFieldTagTooltip() {
document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
if (!tooltip || el.scrollWidth <= el.clientWidth) return
// Show tooltip only on truncate
tippy(el, {
content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
placement: 'top',
allowHTML: true,
arrow: true,
animation: 'fade',
duration: 0,
maxWidth: '200px',
})
})
}
const tooltipInstances: any[] = []
function loadMentionFieldTagTooltip() {
document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
if (!tooltip || el.scrollWidth <= el.clientWidth) return
// Show tooltip only on truncate
const instance = tippy(el, {
content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
placement: 'top',
allowHTML: true,
arrow: true,
animation: 'fade',
duration: 0,
maxWidth: '200px',
})
tooltipInstances.push(instance)
})
}
onBeforeUnmount(() => {
tooltipInstances.forEach(instance => instance.destroy())
tooltipInstances.length = 0
})

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch 2 times, most recently from 9692ad1 to 5559fe2 Compare December 5, 2024 19:55
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (18)
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (1)

687-688: Consider refactoring repeated conditional styling logic

The conditional styling logic is repeated multiple times with similar patterns. This could be refactored into a computed property or helper function to improve maintainability.

Consider refactoring like this:

+ const getStatusClass = (condition: boolean) => ({
+   'bg-nc-bg-green-dark text-nc-content-green-dark': condition,
+   'bg-nc-bg-red-dark text-nc-content-red-dark': !condition
+ })

- class="text-nc-border-gray-dark': !(selectedRecordPk && outputColumnIds.length && inputColumns.length),
-       'text-nc-content-purple-dark': !!(selectedRecordPk && outputColumnIds.length && inputColumns.length)"
+ :class="getStatusClass(selectedRecordPk && outputColumnIds.length && inputColumns.length)"

This would make the code more maintainable and reduce duplication.

Also applies to: 698-702, 711-715, 724-728

packages/nc-gui/components/smartsheet/header/Menu.vue (4)

48-61: Enhance error handling for AI integrations.

The columnInvalid computed property handles validation but could benefit from additional error handling for edge cases.

Consider adding type validation and default values:

 const columnInvalid = computed<{ isInvalid: boolean; tooltip: string }>(() => {
   if (!column?.value) {
     return {
       isInvalid: false,
-      tooltip: '',
+      tooltip: 'Column not initialized',
     }
   }
+  if (!aiIntegrations?.value) {
+    return {
+      isInvalid: false,
+      tooltip: 'AI integrations not available',
+    }
+  }
   return isColumnInvalid(column.value, aiIntegrations.value, isPublic.value || !isUIAllowed('dataEdit'))
 })

431-434: Improve delay handling in copy operation.

The current implementation uses a fixed delay which might not be ideal for all scenarios.

Consider making the delay configurable and using try-finally:

-await ncDelay(5000)
-isFieldIdCopied.value = false
+try {
+  await ncDelay(import.meta.env.VITE_COPY_FEEDBACK_DELAY || 5000)
+} finally {
+  isFieldIdCopied.value = false
+}

451-451: Enhance accessibility for invalid column state.

The UI changes for invalid column state could benefit from improved accessibility.

Add ARIA attributes for better screen reader support:

-<GeneralIcon v-if="columnInvalid.isInvalid && !isExpandedForm" class="text-red-300 w-3.5 h-3.5" icon="alertTriangle" />
+<GeneralIcon 
+  v-if="columnInvalid.isInvalid && !isExpandedForm" 
+  class="text-red-300 w-3.5 h-3.5" 
+  icon="alertTriangle"
+  role="img"
+  :aria-label="$t(columnInvalid.tooltip)"
+/>

Also applies to: 461-461, 464-464


Line range hint 1-24: Consider extracting type definitions.

The component has several complex prop and computed property types that could benefit from being extracted into separate type definitions.

Consider creating a separate types file:

// types/menu.types.ts
export interface ColumnInvalidState {
  isInvalid: boolean
  tooltip: string
}

export interface MenuProps {
  virtual?: boolean
  isOpen: boolean
  isHiddenCol?: boolean
}
packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

130-144: Simplify the handleDisableSubmitBtn logic

The current implementation has nested conditions that can be simplified.

Consider this more concise implementation:

const handleDisableSubmitBtn = () => {
-  if (!isEnabledGenerateText.value) {
-    if (disableSubmitBtn.value) {
-      disableSubmitBtn.value = false
-    }
-    return
-  }
-
-  if (isPreviewEnabled.value) {
-    disableSubmitBtn.value = false
-  } else {
-    disableSubmitBtn.value = true
-  }
+  disableSubmitBtn.value = isEnabledGenerateText.value && !isPreviewEnabled.value
}

156-311: Enhance accessibility for interactive elements

While the UI structure is good, some interactive elements could benefit from improved accessibility.

Consider these improvements:

  1. Add aria-label to switches:
-<NcSwitch v-model:checked="richMode" :disabled="isEnabledGenerateText">
+<NcSwitch 
+  v-model:checked="richMode" 
+  :disabled="isEnabledGenerateText"
+  :aria-label="$t('labels.enableRichText')"
+>
  1. Add aria-live region for the preview section:
-<div v-if="previewRow.row?.[previewFieldTitle]?.value">
+<div 
+  v-if="previewRow.row?.[previewFieldTitle]?.value"
+  aria-live="polite"
+  aria-atomic="true"
+>
packages/nc-gui/components/cell/AI.vue (1)

Line range hint 2151-2151: Correct the function name to 'expandAndLoseFocus'

There's a typographical error in the function name expandAndLooseFocus. The correct spelling is expandAndLoseFocus.

Apply this diff to fix the typo:

-const expandAndLooseFocus = (row: Row, col: Record<string, any>) => {
+const expandAndLoseFocus = (row: Row, col: Record<string, any>) => {

Also, update all references to this function accordingly.

packages/nocodb-sdk/src/lib/helperFunctions.ts (1)

231-245: Enhance type safety and documentation for helper functions.

The new helper functions would benefit from improved type safety and documentation.

Consider applying these improvements:

+/**
+ * Safely parses a JSON string, returning the original value if parsing fails
+ * @param v - The value to parse
+ * @returns The parsed object if successful, otherwise the original value
+ */
-export function parseHelper(v: any): any {
+export function parseHelper<T = unknown>(v: unknown): T | unknown {
   try {
     return typeof v === 'string' ? JSON.parse(v) : v
   } catch {
     return v
   }
 }

+/**
+ * Safely stringifies a value to JSON, returning the original value if stringification fails
+ * @param v - The value to stringify
+ * @returns The JSON string if successful, otherwise the original value
+ */
-export function stringifyHelper(v: any): string {
+export function stringifyHelper(v: unknown): string {
   try {
-    return JSON.stringify(v)
+    return typeof v === 'undefined' ? 'undefined' : JSON.stringify(v)
   } catch {
     return v as string
   }
 }
packages/nocodb/src/db/sortV2.ts (1)

202-232: Extract database-specific JSON extraction logic.

The database-specific JSON extraction logic should be moved to a separate function for better maintainability.

Consider extracting the logic:

+function getJsonValueExtractor(knex: Knex, columnName: string) {
+  if (knex.clientType() === 'pg') {
+    return knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [columnName]);
+  } else if (knex.clientType().startsWith('mysql')) {
+    return knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [columnName]);
+  } else if (knex.clientType() === 'sqlite3') {
+    return knex.raw(`json_extract(??, '$.value')`, [columnName]);
+  } else if (knex.clientType() === 'mssql') {
+    return knex.raw(`JSON_VALUE(??, '$.value')`, [columnName]);
+  }
+  return null;
+}

 case UITypes.LongText: {
   if (isAIPromptCol(column)) {
-    let col;
-    if (knex.clientType() === 'pg') {
-      col = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [
-        column.column_name,
-      ]);
-    } else if (knex.clientType().startsWith('mysql')) {
-      col = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [
-        column.column_name,
-      ]);
-    } else if (knex.clientType() === 'sqlite3') {
-      col = knex.raw(`json_extract(??, '$.value')`, [
-        column.column_name,
-      ]);
-    } else if (knex.clientType() === 'mssql') {
-      col = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
-    }
+    const col = getJsonValueExtractor(knex, column.column_name);
     qb.orderBy(col, sort.direction || 'asc', nulls);
   } else {
     qb.orderBy(
packages/nc-gui/components/ai/PromptWithFields.vue (1)

Line range hint 141-157: Improve editor focus handling.

The focus handling could be more robust by considering the readOnly state and potential race conditions.

Consider these improvements:

 onMounted(async () => {
   await until(() => vModel.value !== null && vModel.value !== undefined).toBeTruthy()
 
   // replace {id} with <span data-type="mention" data-id="id"></span>
   const renderContent = vModel.value
     .replace(/\{(.*?)\}/g, '<span data-type="mention" data-id="$1"></span>')
     .trim()
     .replace(/\n/g, '<br>')
 
   editor.value?.commands.setContent(renderContent)
 
-  if (autoFocus.value) {
+  if (autoFocus.value && !readOnly.value && editor.value) {
     setTimeout(() => {
-      editor.value?.chain().focus().setTextSelection(vModel.value.length).run()
+      editor.value.chain().focus().setTextSelection(vModel.value.length).run()
     }, 100)
   }
 })
packages/nc-gui/components/ai/Settings.vue (1)

70-73: Consider adding a comment explaining the early return

The early return when isEditColumn is true could benefit from a brief comment explaining why we skip initialization in edit mode.

+ // Skip initialization in edit mode as the values are already set
  if (isEditColumn.value) {
    return
  }
packages/nc-gui/utils/columnUtils.ts (1)

269-278: Consider splitting the validation logic

The isColumnInvalid function is becoming complex with multiple responsibilities. Consider extracting AI-specific validation into a separate function.

+ const isAiColumnInvalid = (
+   colOptions: ButtonType,
+   aiIntegrations: Partial<IntegrationType>[],
+   isReadOnly: boolean
+ ): { isInvalid: boolean; tooltip: string } => {
+   return {
+     isInvalid:
+       !colOptions.fk_integration_id ||
+       (isReadOnly
+         ? false
+         : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')),
+     tooltip: 'title.aiIntegrationMissing',
+   }
+ }
packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (1)

832-872: Consider extracting database-specific SQL queries into constants

The implementation correctly handles different database types, but the SQL queries could be extracted into constants for better maintainability and reusability.

+ const DB_SPECIFIC_VALUE_EXTRACTORS = {
+   pg: (colName: string) => `TRIM('"' FROM (${colName}::jsonb->>'value'))`,
+   mysql: (colName: string) => `JSON_UNQUOTE(JSON_EXTRACT(${colName}, '$.value'))`,
+   sqlite3: (colName: string) => `json_extract(${colName}, '$.value')`,
+   mssql: (colName: string) => `JSON_VALUE(${colName}, '$.value')`
+ };

case UITypes.LongText: {
  if (col.meta?.[LongTextAiMetaProp] === true) {
-   if (knex.clientType() === 'pg') {
-     aliasToColumn[col.id] = async (): Promise<any> => ({
-       builder: knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [col.column_name]),
-     });
-   } else if (knex.clientType().startsWith('mysql')) {
-     aliasToColumn[col.id] = async (): Promise<any> => ({
-       builder: knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [col.column_name]),
-     });
-   } else if (knex.clientType() === 'sqlite3') {
-     aliasToColumn[col.id] = async (): Promise<any> => ({
-       builder: knex.raw(`json_extract(??, '$.value')`, [col.column_name]),
-     });
-   } else if (knex.clientType() === 'mssql') {
-     aliasToColumn[col.id] = async (): Promise<any> => ({
-       builder: knex.raw(`JSON_VALUE(??, '$.value')`, [col.column_name]),
-     });
-   }
+   const dbType = knex.clientType();
+   const extractor = DB_SPECIFIC_VALUE_EXTRACTORS[dbType];
+   if (extractor) {
+     aliasToColumn[col.id] = async (): Promise<any> => ({
+       builder: knex.raw(extractor('??'), [col.column_name]),
+     });
+   }
  } else {
    aliasToColumn[col.id] = () => Promise.resolve({ builder: col.column_name });
  }
  break;
}
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (2)

Line range hint 249-267: Consider using a type map for preload configurations

The type selection logic could be more maintainable by using a map of type configurations.

+ const TYPE_PRELOAD_CONFIG = {
+   [AIButton]: {
+     uidt: UITypes.Button,
+     preload: { type: ButtonActionsType.Ai }
+   },
+   [AIPrompt]: {
+     uidt: UITypes.LongText,
+     preload: { meta: { [LongTextAiMetaProp]: true } }
+   }
+ };

const  UITypes | typeof AIButton | typeof AIPrompt, fromSearchList = false) => {
  if (fromSearchList && !isEdit.value && aiAutoSuggestMode.value) {
    onInit()
  }

-  if (uidt === AIButton) {
-    formState.value.uidt = UITypes.Button
-    preload = {
-      type: ButtonActionsType.Ai,
-    }
-  } else if (uidt === AIPrompt) {
-    formState.value.uidt = UITypes.LongText
-    preload = {
-      meta: {
-        [LongTextAiMetaProp]: true,
-      },
-    }
+  const config = TYPE_PRELOAD_CONFIG[uidt];
+  if (config) {
+    formState.value.uidt = config.uidt;
+    preload = config.preload;
  } else {
    formState.value.uidt = uidt
  }
  onUidtOrIdTypeChange(preload)
}

1070-1076: Consider using a computed property for icon selection

The icon selection logic in the template could be moved to a computed property for better maintainability and reusability.

+ const getTypeIcon = computed(() => (opt: { name: string }) => {
+   if (isAiButtonSelectOption(opt.name) && !isColumnTypeOpen.value) {
+     return iconMap.cellAiButton;
+   }
+   if (isAiPromptSelectOption(opt.name) && !isColumnTypeOpen.value) {
+     return iconMap.cellAi;
+   }
+   return opt.icon;
+ });

- :is="
-   isAiButtonSelectOption(opt.name) && !isColumnTypeOpen
-     ? iconMap.cellAiButton
-     : isAiPromptSelectOption(opt.name) && !isColumnTypeOpen
-     ? iconMap.cellAi
-     : opt.icon
- "
+ :is="getTypeIcon(opt)"
packages/nocodb/src/db/BaseModelSqlv2.ts (2)

10062-10104: Add type safety and validation for AI prompt data

The AI prompt data handling logic could benefit from stronger type safety and validation of the input data structure.

Consider adding TypeScript interfaces and validation:

interface AiPromptData {
  value: string;
  lastModifiedBy?: string | null;
  lastModifiedTime?: string | null;
  isStale?: boolean;
}

// Then validate the data structure:
if (data[column.column_name]) {
  let value = data[column.column_name];
  
  // Validate input
  if (typeof value === 'object' && !value.value) {
    throw new Error('AI prompt data must contain a value property');
  }

  if (typeof value === 'object') {
    value = value.value;
  }

  if (typeof value !== 'string') {
    throw new Error('AI prompt value must be a string');
  }

  const obj: AiPromptData = {
    value,
    lastModifiedBy: null,
    lastModifiedTime: null,
    isStale: false
  };
  // ... rest of the logic
}

10107-10137: Optimize AI column dependency tracking and add error handling

The AI column isStale handling could be optimized for performance and include better error handling for edge cases.

  1. Cache the referenced column IDs to avoid repeated regex matching:
// Add column option type
interface AiColumnOptions {
  prompt: string;
  referencedColumnIds?: string[]; // Cache after first extraction
}

// Cache referenced columns on first access
if (!aiColumn.colOptions.referencedColumnIds) {
  aiColumn.colOptions.referencedColumnIds = aiColumn.colOptions.prompt
    ?.match(/{(.*?)}/g)
    ?.map(id => id.replace(/{|}/g, ''))
    ?? [];
}

const referencedColumnIds = aiColumn.colOptions.referencedColumnIds;
  1. Add validation for referenced columns:
const referencedColumns = referencedColumnIds
  .map(id => {
    const col = this.model.columnsById[id];
    if (!col) {
      logger.warn(`Referenced column ${id} not found in AI prompt ${aiColumn.title}`);
    }
    return col;
  })
  .filter(Boolean);
  1. Consider adding a debounce mechanism to avoid excessive isStale updates when multiple referenced columns change rapidly.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0a49d91 and 5559fe2.

📒 Files selected for processing (72)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/dashboard/settings/base/index.vue (0 hunks)
  • packages/nc-gui/components/feed/View.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/calendar/MonthView.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (5 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (5 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
  • packages/nc-gui/components/dashboard/settings/base/index.vue
🚧 Files skipped from review as they are similar to previous changes (46)
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nocodb/src/models/index.ts
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/components/feed/View.vue
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/general/Loader.vue
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue
  • packages/nocodb/src/models/Integration.ts
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/smartsheet/calendar/MonthView.vue
  • packages/nc-gui/components/smartsheet/Kanban.vue
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/cell/TextArea.vue
👮 Files not reviewed due to content moderation or server errors (2)
  • packages/nc-gui/components/smartsheet/details/Fields.vue
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (38)
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (2)

369-369: LGTM: AI Settings integration

The is-edit-column prop is correctly implemented and follows Vue.js naming conventions.


471-474: LGTM: Tooltip implementation enhances UX

The tooltip implementation with show-on-truncate-only is a good UX practice, showing additional information only when the text is truncated.

packages/nc-gui/components/smartsheet/header/Menu.vue (1)

52-61: Verify error handling in async operations.

The component contains multiple async operations that should be verified for proper error handling.

Also applies to: 431-434

packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (4)

4-8: LGTM: Props and emits follow Vue 3 conventions

The component correctly implements the Vue 3 v-model pattern using modelValue prop and update:modelValue emit.


314-340: LGTM: Well-structured styles

The styles are properly scoped and effectively use Tailwind utilities along with deep selectors for child component styling.


48-58: ⚠️ Potential issue

Initialize vModel.value.meta before assignment

The setter of isEnabledGenerateText could throw an error if vModel.value.meta is undefined.

Apply this fix:

set: (value: boolean) => {
+  if (!vModel.value.meta) vModel.value.meta = {}
   vModel.value.meta[LongTextAiMetaProp] = value
   vModel.value.prompt_raw = ''
   previewRow.value.row = {}
   isAlreadyGenerated.value = false
},

66-104: ⚠️ Potential issue

Add error handling for the generateRows asynchronous call

The generate function should handle potential errors from the generateRows call.

Apply this fix:

const generate = async () => {
  generatingPreview.value = true

  await loadViewData()

  const pk = formattedData.value.length ? extractPkFromRow(unref(formattedData.value[0].row), meta.value?.columns || []) : ''

  if (!formattedData.value.length || !pk) {
    message.error('Include at least 1 sample record in table to generate')
    generatingPreview.value = false
    return
  }

  previewFieldTitle.value = vModel.value?.title || 'temp_title'

+ try {
    const res = await generateRows(
      meta.value.id!,
      {
        title: previewFieldTitle.value,
        prompt_raw: vModel.value.prompt_raw,
        fk_integration_id: vModel.value.fk_integration_id,
        uidt: UITypes.LongText,
      },
      [pk],
    )

    if (res?.length && res[0]?.[previewFieldTitle.value]) {
      previewRow.value.row = {
        ...res[0],
        [previewFieldTitle.value]: {
          value: res[0]?.[previewFieldTitle.value],
        },
      }
      isAlreadyGenerated.value = true
    }
+ } catch (error) {
+   message.error(`Failed to generate preview: ${error.message}`)
+ }
  generatingPreview.value = false
}
packages/nc-gui/components/smartsheet/grid/Table.vue (3)

8-8: Good use of 'isAIPromptCol' function

Importing isAIPromptCol enhances the clarity and maintainability of AI prompt column checks.


1061-1061: Addition of default parameter enhances flexibility

Adding a default value for _scrollBehaviour in scrollToCell function improves its usability.


2140-2146: ⚠️ Potential issue

Fix incorrect self-closing <component> tag

The <component> tag is incorrectly self-closed. In Vue.js, <component> should use a closing tag to encapsulate its content.

Apply this diff to fix the issue:

-                                 <component
-                                   :is="iconMap.maximize"
-                                   v-if="expandForm"
-                                   class="select-none nc-row-expand opacity-90 w-4 h-4"
-                                   @click="expandAndLooseFocus(row, state)"
-                                 />
+                                 <component
+                                   :is="iconMap.maximize"
+                                   v-if="expandForm"
+                                   class="select-none nc-row-expand opacity-90 w-4 h-4"
+                                   @click="expandAndLooseFocus(row, state)"
+                                 ></component>

Likely invalid or redundant comment.

packages/nc-gui/components/cell/AI.vue (1)

131-135: Fix the tooltip disabled logic

The tooltip's :disabled condition is incorrect. The tooltip should be disabled when the button is enabled (i.e., not disabled).

Apply this diff to fix the logic:

-    <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+    <NcTooltip :disabled="!isDisabledAiButton" class="flex">
packages/nc-gui/components/ai/PromptWithFields.vue (1)

159-174: Add tooltip cleanup on component unmount.

The tooltips are properly initialized but need cleanup to prevent memory leaks.

packages/nc-gui/components/ai/Settings.vue (3)

10-15: LGTM: Props definition is clear and well-structured

The new isEditColumn prop with its default value is properly defined.


215-221: LGTM: Improved select styling

The scoped style addition improves visual consistency with rounded corners.


84-86: Verify model initialization logic

The condition for model initialization has been modified. Ensure this doesn't break existing functionality.

✅ Verification successful

Model initialization logic is safe

After reviewing the code, the modified condition is actually an improvement in the initialization logic. The onIntegrationChange is called only when either:

  1. No model is selected (!vModel.value), or
  2. No available models are loaded (!availableModels.value.length)

This ensures models are properly loaded when needed while avoiding unnecessary API calls when a model is already selected and models are available. The logic maintains proper initialization while being more efficient.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any existing usages of this component to verify the initialization pattern
rg -A 5 "Settings\.vue|<Settings" --type vue

Length of output: 74


Script:

#!/bin/bash
# Search for the file and check its content
cat "packages/nc-gui/components/ai/Settings.vue"

Length of output: 7716


Script:

#!/bin/bash
# Search for any imports or usage of this component
rg -l "Settings" --type-add 'vue:*.vue' --type vue

Length of output: 2223


Script:

#!/bin/bash
# Look for any references to onIntegrationChange
rg "onIntegrationChange" --type-add 'vue:*.vue' --type vue

Length of output: 403

packages/nc-gui/composables/useNocoAi.ts (2)

20-23: LGTM: Type refinement and computed property

The change from IntegrationType[] to Partial<IntegrationType>[] allows for more flexible integration definitions, and the computed property provides a clean way to check availability.


24-28: ⚠️ Potential issue

Fix computed property access in isAiIntegrationAvailableInList

The function has a bug where it's using the computed property name instead of its value.

-    if (!aiIntegrationAvailable) return false
+    if (!aiIntegrationAvailable.value) return false
packages/nc-gui/utils/columnUtils.ts (1)

289-296: Verify AI integration validation logic

The AI integration validation for buttons needs thorough testing, especially the edge cases with isReadOnly.

✅ Verification successful

Based on the gathered information, I can now provide a final response:

AI integration validation logic is correctly implemented

The validation logic for AI button type is properly implemented with appropriate checks:

  • The ncIsArrayIncludes helper correctly validates if the integration ID exists in available integrations
  • The isReadOnly flag is properly handled to bypass validation in read-only mode
  • The validation is used consistently across the codebase, particularly in the Button component
  • Error handling includes appropriate tooltip messaging for missing integrations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of this validation in button components
rg -A 5 "isColumnInvalid.*Button.*Ai" --type vue --type ts

Length of output: 88


Script:

#!/bin/bash
# Search for usages of isColumnInvalid with Ai type
rg -A 5 "isColumnInvalid.*Ai"

# Search for AI button validation related code
rg -A 5 "ButtonActionsType.Ai"

# Look for AI integration validation patterns
rg -A 5 "fk_integration_id.*aiIntegrations"

Length of output: 12100


Script:

#!/bin/bash
# Search for the implementation of ncIsArrayIncludes function
rg -A 5 "ncIsArrayIncludes.*="

# Look for the validation usage in Button component
ast-grep --pattern 'const isFieldAiIntegrationAvailable = computed(() => { $$$ })'

Length of output: 545

packages/nc-gui/composables/useIntegrationsStore.ts (5)

93-94: LGTM!

Clean addition of the aiIntegrations import for AI integration management.


191-201: LGTM!

Good implementation of event tracking and AI integration cleanup on deletion.


300-309: LGTM!

Clean implementation of AI integration creation with all required properties.


229-238: 🛠️ Refactor suggestion

Avoid mutating objects within map—return new objects instead

Mutating objects within the map function can lead to unexpected behavior in reactive systems. Instead, return new objects to ensure reactivity is preserved.

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.title = integration.title
-     return i
+     return { ...i, title: integration.title }
    }
    return i
  })
}

Likely invalid or redundant comment.


258-269: 🛠️ Refactor suggestion

Avoid mutating objects within map—return new objects instead

Similar to the previous comment, mutate objects immutably within the map function to maintain reactivity.

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.is_default = true
+     return { ...i, is_default: true }
    } else {
-     i.is_default = false
+     return { ...i, is_default: false }
    }
-     return i
  })
}

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (3)

186-196: LGTM!

Clean implementation of the UI filter logic for AI prompt and button types.


600-602: LGTM!

Clean implementation of the AI prompt type check.


1137-1141: LGTM!

Good implementation of the LongText options component with navigation handling.

packages/nocodb/src/db/conditionV2.ts (1)

596-612: LGTM: Database-specific JSON value extraction for AI prompt columns

The implementation correctly handles JSON value extraction for AI prompt columns across different database types:

  • PostgreSQL: Uses jsonb->>'value'
  • MySQL: Uses JSON_UNQUOTE(JSON_EXTRACT())
  • SQLite: Uses json_extract()
  • MSSQL: Uses JSON_VALUE()
packages/nocodb/src/models/Column.ts (6)

452-469: LGTM: AI column option insertion

The implementation correctly handles AI column option insertion when the column is marked as an AI prompt column via metadata.


573-577: LGTM: AI column option retrieval

The implementation correctly retrieves AI column options when the column is marked as an AI prompt column.


Line range hint 1244-1280: LGTM: Button column workspace handling

The implementation correctly handles workspace and integration IDs for button columns when importing across different workspaces.


Line range hint 1281-1296: LGTM: AI prompt column workspace handling

The implementation correctly handles workspace and integration IDs for AI prompt columns when importing across different workspaces.


1353-1369: LGTM: AI column option deletion

The implementation correctly handles cleanup of AI column options during column updates.


886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect. It should check for the actual column ID to detect whether the prompt includes the deleted column ID.

Apply this diff to fix the placeholder matching:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (2)

182-182: LGTM: AI prompt column filtering

The implementation correctly filters out AI prompt columns during import to handle them separately.


1113-1114: LGTM: AI prompt column handling in referenced columns

The implementation correctly includes AI prompt columns in the referenced column set for proper handling during import.

packages/nocodb/src/db/BaseModelSqlv2.ts (1)

5721-5723: ⚠️ Potential issue

Add await to Promise.all for proper async handling

The current code does not properly await the async prepareNocoData calls inside map(), which could lead to unhandled promise rejections.

Apply this diff to fix the async handling:

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.

packages/nocodb/src/services/columns.service.ts (2)

1562-1629: LGTM: Comprehensive AI prompt column handling in columnUpdate

The implementation properly:

  1. Detects changes in AI prompt status
  2. Converts data between AI record types
  3. Handles prompt substitution with proper error handling

2266-2288: LGTM: Proper AI prompt handling in columnAdd

The implementation correctly:

  1. Validates and processes AI prompts
  2. Substitutes column aliases with IDs
  3. Provides clear error messages for missing fields

insertObj,
);

return this.read(context, longTextColumn.fk_column_id, ncMeta);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
📝 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.

Suggested change
return this.read(context, longTextColumn.fk_column_id, ncMeta);
return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

await NocoCache.set(`${CacheScope.COL_LONG_TEXT}:${columnId}`, column);
}

return column ? this.castType(column) : null;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;
📝 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.

Suggested change
return column ? this.castType(column) : null;
return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

Comment on lines 299 to 309
case UITypes.LongText: {
if (parseProp(col.meta)[LongTextAiMetaProp]) {
const colOptions = col.colOptions as ButtonType

result.isInvalid =
!colOptions.fk_integration_id ||
(isReadOnly
? false
: !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))

result.tooltip = 'title.aiIntegrationMissing'
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider DRYing up duplicate AI validation logic

The AI integration validation logic is duplicated between Button and LongText types. Consider using the suggested isAiColumnInvalid helper function.

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch 3 times, most recently from 5616a30 to 68ce927 Compare December 7, 2024 06:13
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (12)
packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (1)

42-42: LGTM: Good separation of concerns

Moving the integration loading logic to the store improves code organization and maintainability.

packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: Consider reducing the copy feedback duration

While adding a delay improves the UX by showing the copy confirmation, 5000ms (5 seconds) might be too long. Consider reducing it to 2000-3000ms for a more responsive feel, which is the standard duration for such feedback.

- await ncDelay(5000)
+ await ncDelay(2000)
packages/nc-gui/components/cell/TextArea.vue (1)

Line range hint 421-470: Consider extracting AI status component

The AI status section contains complex conditional rendering and repeated user display logic. Consider extracting this into a separate component for better maintainability.

// AIStatusDisplay.vue
<template>
  <div class="flex items-center gap-2 px-3 py-0.5 !text-small leading-[18px]">
    <span class="text-nc-content-purple-light truncate">Generated by AI</span>
    <slot name="edit-status" />
    <slot name="regenerate-button" />
  </div>
</template>
packages/nc-gui/composables/useIntegrationsStore.ts (3)

301-309: Consider extracting AI integration object creation to a helper function

The AI integration object creation could be made more maintainable by extracting it to a helper function.

Consider refactoring as follows:

+ const createAiIntegrationObject = (integration: IntegrationType) => ({
+   id: integration.id,
+   title: integration.title,
+   is_default: integration.is_default,
+   type: integration.type,
+   sub_type: integration.sub_type,
+ })

  if (response.type === IntegrationsType.Ai) {
-   aiIntegrations.value.push({
-     id: response.id,
-     title: response.title,
-     is_default: response.is_default,
-     type: response.type,
-     sub_type: response.sub_type,
-   })
+   aiIntegrations.value.push(createAiIntegrationObject(response))
  }

191-192: Consider enriching event tracking with additional context

The event tracking is well-placed but could provide more valuable insights by including additional properties.

Consider adding relevant properties to the events, such as:

  • Integration type
  • Integration sub-type
  • Operation status (success/failure)

Example:

- $e('a:integration:delete')
+ $e('a:integration:delete', {
+   type: integration.type,
+   subType: integration.sub_type,
+ })

Also applies to: 224-225, 253-254, 287-291


Line range hint 447-496: Add error handling and improve type safety

The loadDynamicIntegrations function could benefit from several improvements:

  1. Missing error handling
  2. Type assertion could be replaced with proper typing
  3. Complex icon handling logic could be extracted

Consider refactoring as follows:

+ interface DynamicIntegration {
+   type: IntegrationsType
+   subType: string
+   meta: {
+     title?: string
+     icon?: string
+     description?: string
+     order?: number
+   }
+ }

+ const resolveIntegrationIcon = (icon?: string) => {
+   if (!icon) return iconMap.puzzle
+   if (icon in iconMap) return iconMap[icon as keyof typeof iconMap]
+   if (isValidURL(icon)) {
+     return h('img', {
+       src: icon,
+       alt: di.meta.title || di.subType,
+     })
+   }
+   return iconMap.puzzle
+ }

  const loadDynamicIntegrations = async () => {
    if (integrationsInitialized.value) return
    integrationsInitialized.value = true

+   try {
-     const dynamicIntegrations = (await $api.integrations.list()) as {
-       type: IntegrationsType
-       subType: string
-       meta: {
-         title?: string
-         icon?: string
-         description?: string
-         order?: number
-       }
-     }[]
+     const dynamicIntegrations = await $api.integrations.list() as DynamicIntegration[]

      dynamicIntegrations.sort((a, b) => (a.meta.order ?? Infinity) - (b.meta.order ?? Infinity))

      for (const di of dynamicIntegrations) {
-       let icon: FunctionalComponent<SVGAttributes, {}, any, {}> | VNode
-       if (di.meta.icon) {
-         if (di.meta.icon in iconMap) {
-           icon = iconMap[di.meta.icon as keyof typeof iconMap]
-         } else {
-           if (isValidURL(di.meta.icon)) {
-             icon = h('img', {
-               src: di.meta.icon,
-               alt: di.meta.title || di.subType,
-             })
-           }
-         }
-       } else {
-         icon = iconMap.puzzle
-       }
+       const icon = resolveIntegrationIcon(di.meta.icon)

        const integration: IntegrationItemType = {
          title: di.meta.title || di.subType,
          subType: di.subType,
          icon,
          type: di.type,
          isAvailable: true,
          dynamic: true,
        }

        allIntegrations.push(integration)
        integrationsRefreshKey.value++
      }
+   } catch (e) {
+     console.error('Failed to load dynamic integrations:', e)
+     throw e
+   }
  }
packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

130-144: Simplify the submit button state logic

The current implementation can be made more readable by reducing nesting and simplifying the conditions.

Consider this refactor:

const handleDisableSubmitBtn = () => {
-  if (!isEnabledGenerateText.value) {
-    if (disableSubmitBtn.value) {
-      disableSubmitBtn.value = false
-    }
-    return
-  }
-
-  if (isPreviewEnabled.value) {
-    disableSubmitBtn.value = false
-  } else {
-    disableSubmitBtn.value = true
-  }
+  disableSubmitBtn.value = isEnabledGenerateText.value && !isPreviewEnabled.value
}

156-308: Enhance accessibility for interactive elements

Several interactive elements could benefit from ARIA attributes and keyboard navigation improvements.

Consider these improvements:

  1. Add aria-label to the settings button:
-            <NcButton size="xs" theme="ai" class="!px-1" type="text">
+            <NcButton size="xs" theme="ai" class="!px-1" type="text" aria-label="AI Settings">
  1. Add aria-live region for the preview section:
-      <div v-if="aiIntegrationAvailable && isEnabledGenerateText" class="nc-ai-options-preview overflow-hidden">
+      <div v-if="aiIntegrationAvailable && isEnabledGenerateText" class="nc-ai-options-preview overflow-hidden" aria-live="polite">
  1. Add role and aria-label to the preview container:
-          <div v-if="previewRow.row?.[previewFieldTitle]?.value">
+          <div v-if="previewRow.row?.[previewFieldTitle]?.value" role="region" aria-label="AI Generated Preview">
packages/nc-gui/components/cell/AI.vue (1)

111-112: Consider using object spread operator directly.

The current implementation creates an unnecessary temporary object.

-vModel.value = { ...vModel.value }
+vModel.value = { ...(vModel.value || {}) }
packages/nc-gui/components/ai/PromptWithFields.vue (1)

63-68: Consider optimizing the filter condition.

The current implementation might be inefficient for large lists. Consider using early returns or memoization.

 return (
   props.options?.filter(
-    (o) =>
-      o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
+    (o) => {
+      if (!o.title) return false
+      const lowerTitle = o.title.toLowerCase()
+      const lowerQuery = query.toLowerCase()
+      return lowerTitle.includes(lowerQuery) || `${lowerTitle}}` === lowerQuery
+    }
   ) ?? []
 )
packages/nc-gui/components/ai/Settings.vue (1)

Line range hint 70-86: Consider adding error handling for initialization.

The initialization logic lacks error handling for edge cases.

 onMounted(async () => {
+  try {
     if (isEditColumn.value) {
       return
     }

     if (!vFkIntegrationId.value) {
       if (aiIntegrations.value.length > 0 && aiIntegrations.value[0].id) {
         vFkIntegrationId.value = aiIntegrations.value[0].id
         nextTick(() => {
           onIntegrationChange()
         })
       }
     } else {
       lastIntegrationId.value = vFkIntegrationId.value

       if (!vModel.value || !availableModels.value.length) {
         onIntegrationChange()
       }
     }
+  } catch (error) {
+    console.error('Error during Settings initialization:', error)
+    // Consider showing a user-friendly error message
+  }
 })
packages/nocodb/src/services/columns.service.ts (1)

1578-1645: Robust AI prompt column update handling

The implementation:

  1. Properly handles conversion between AI and non-AI column types
  2. Correctly processes column references in prompts
  3. Maintains data integrity during type changes

Consider extracting the prompt processing logic into a separate utility function to improve reusability and maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5559fe2 and 68ce927.

📒 Files selected for processing (69)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (44)
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/models/index.ts
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nocodb/src/models/Integration.ts
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nc-gui/components/general/Loader.vue
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nc-gui/components/smartsheet/Kanban.vue
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nocodb/src/models/Column.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts
  • packages/nc-gui/components/smartsheet/details/Fields.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (44)
packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (1)

216-217: ⚠️ Potential issue

Add error handling for dynamic integration loading

The loadDynamicIntegrations call should include error handling to gracefully handle loading failures and provide user feedback.

 onMounted(() => {
-  loadDynamicIntegrations()
+  try {
+    await loadDynamicIntegrations()
+  } catch (error) {
+    // Handle error appropriately (e.g., show error notification)
+    console.error('Failed to load integrations:', error)
+  }

   if (!integrationsCategoryFilter.value.length) {
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

Line range hint 1-458: Implementation looks solid!

The code demonstrates good practices with:

  • Comprehensive error handling and user feedback
  • Proper type safety and permission checks
  • Well-documented solutions for known issues
  • Clear separation of concerns
packages/nc-gui/components/cell/TextArea.vue (5)

104-111: LGTM: Well-structured computed property

The isAiGenerating computed property is well-structured with proper null checks and logical conditions.


Line range hint 408-421: LGTM: Clear and informative AI warning message

The warning message for stale AI-generated content is well-structured and provides clear information to users.


524-527: Remove invalid Tailwind class

The class animate-infinite is not a valid Tailwind CSS utility class. The animate-spin class already provides an infinite animation.

-:class="{ 'animate-infinite animate-spin': isAiGenerating }"
+:class="{ 'animate-spin': isAiGenerating }"

Line range hint 712-723: LGTM: Well-structured CSS for expanded form

The CSS for handling the expanded form state is well-organized and uses appropriate selectors.


100-103: ⚠️ Potential issue

Replace non-null assertions with safe property access

Using non-null assertions (!) can lead to runtime errors if meta.value or columns are undefined.

-return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
+return currentRow.value?.row && meta.value?.columns
+  ? extractPkFromRow(currentRow.value.row, meta.value.columns)
+  : undefined

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (4)

1-25: LGTM! Component setup follows Vue 3 best practices

The component properly defines its props, emits, and dependencies. The availableFields computed property correctly filters columns based on valid criteria.


314-340: LGTM! Well-organized styles

The styles follow Vue scoped CSS best practices and properly use the deep selector for targeting child components.


48-58: ⚠️ Potential issue

Initialize vModel.value.meta before assignment

The setter of isEnabledGenerateText could cause runtime errors if vModel.value.meta is undefined.

Apply this fix:

set: (value: boolean) => {
+  if (!vModel.value.meta) vModel.value.meta = {}
   vModel.value.meta[LongTextAiMetaProp] = value
   vModel.value.prompt_raw = ''
   previewRow.value.row = {}
   isAlreadyGenerated.value = false
},

66-104: ⚠️ Potential issue

Add error handling for the generateRows asynchronous call

The generate function needs error handling to gracefully handle failures.

Apply this fix:

const generate = async () => {
  generatingPreview.value = true

  await loadViewData()

  const pk = formattedData.value.length ? extractPkFromRow(unref(formattedData.value[0].row), meta.value?.columns || []) : ''

  if (!formattedData.value.length || !pk) {
    message.error('Include at least 1 sample record in table to generate')
    generatingPreview.value = false
    return
  }

  previewFieldTitle.value = vModel.value?.title || 'temp_title'

+ try {
    const res = await generateRows(
      meta.value.id!,
      {
        title: previewFieldTitle.value,
        prompt_raw: vModel.value.prompt_raw,
        fk_integration_id: vModel.value.fk_integration_id,
        uidt: UITypes.LongText,
      },
      [pk],
    )

    if (res?.length && res[0]?.[previewFieldTitle.value]) {
      previewRow.value.row = {
        ...res[0],
        [previewFieldTitle.value]: {
          value: res[0]?.[previewFieldTi
10000
tle.value],
        },
      }
      isAlreadyGenerated.value = true
    }
+ } catch (error) {
+   message.error(`Failed to generate preview: ${error.message}`)
+ }
  generatingPreview.value = false
}
packages/nc-gui/components/cell/AI.vue (4)

16-19: LGTM: Role and public status checks added.

The addition of isUIAllowed and isPublic injection provides proper access control for AI features.


118-120: LGTM: Consolidated button disable logic.

The isDisabledAiButton computed property effectively consolidates multiple conditions into a single, readable check.


131-139: Fix tooltip disabled logic.

The tooltip's disabled condition seems incorrect. It should be disabled when the button is enabled, not the other way around.

-    <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+    <NcTooltip :disabled="!isDisabledAiButton" class="flex">

160-171: LGTM: Enhanced button and focus styles.

The updated styles improve visual feedback and accessibility through better focus states and consistent styling.

Also applies to: 184-187

packages/nc-gui/components/ai/PromptWithFields.vue (2)

Line range hint 19-31: LGTM: Read-only mode implementation.

The readOnly prop is properly implemented with a default value and correctly referenced in the component setup.

Also applies to: 44-46


159-174: Add tooltip cleanup on component unmount.

The tooltips are properly initialized, but they should be cleaned up when the component is unmounted to prevent memory leaks.

+ const tooltipInstances: any[] = []

  function loadMentionFieldTagTooltip() {
    document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
      const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
      if (!tooltip || el.scrollWidth <= el.clientWidth) return
      // Show tooltip only on truncate
-     tippy(el, {
+     const instance = tippy(el, {
        content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
        placement: 'top',
        allowHTML: true,
        arrow: true,
        animation: 'fade',
        duration: 0,
        maxWidth: '200px',
      })
+     tooltipInstances.push(instance)
    })
  }

+ onBeforeUnmount(() => {
+   tooltipInstances.forEach(instance => instance.destroy())
+   tooltipInstances.length = 0
+ })
packages/nc-gui/components/ai/Settings.vue (1)

10-15: LGTM: Edit column mode properly implemented.

The isEditColumn prop is correctly added with appropriate default value and type safety.

Also applies to: 24-25

packages/nc-gui/composables/useNocoAi.ts (3)

20-20: LGTM: Type change improves flexibility.

The change from IntegrationType[] to Partial<IntegrationType>[] is a good improvement as it allows for partial integration objects.


22-22: LGTM: Clear and concise computed property.

The computed property provides a clean way to check for AI integration availability.


24-28: ⚠️ Potential issue

Fix the condition in isAiIntegrationAvailableInList

The function has a bug where it's using the computed property name instead of its value.

-    if (!aiIntegrationAvailable) return false
+    if (!aiIntegrationAvailable.value) return false
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (3)

369-369: Verify prop name in AiSettings component

The prop :is-edit-column="isEdit" might not match the expected prop name in the AiSettings component.

#!/bin/bash
# Description: Check prop name in AiSettings component
# Search for prop definition in AiSettings component
ast-grep --pattern 'defineProps<{
  $$$
  isEditColumn?: $_
  $$$
}>()'

471-475: LGTM: Enhanced accessibility with tooltips.

The addition of tooltips for truncated text improves the user experience.


687-688: LGTM: Well-structured button state management.

The implementation properly handles button states with:

  • Clear conditional classes
  • Consistent color scheme
  • Comprehensive state conditions

Also applies to: 698-714, 724-730

packages/nocodb/src/models/AIColumn.ts (2)

26-26: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing because this refers to the class itself in a static context. It's clearer and more explicit to reference the class name directly.

Apply this diff to fix the issue:

-        return this._insert(
+        return AIColumn._insert(
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing because this refers to the class itself. It's better to use the class name directly for clarity.

Apply this diff to fix the issue:

-        return this._update(
+        return AIColumn._update(
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: Avoid using 'this' in static methods; use the class name instead

In static methods, using this can be confusing. It's clearer to reference the class name directly to indicate that the method belongs to the class, not an instance.

Apply this diff to fix the issue:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: Avoid using 'this' in static methods; use the class name instead

Using this in a static context can lead to confusion. It's best practice to use the class name directly for clarity.

Apply this diff to fix the issue:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

135-149: Avoid string concatenation in SQL queries to prevent SQL injection

In the MSSQL query, string concatenation is used to build the JSON string. This approach can lead to SQL injection vulnerabilities. It's important to use parameterized queries to ensure security.

Consider refactoring the query to use parameter bindings or MSSQL's JSON functions without concatenation. Here's a suggested fix:

-          `UPDATE ??
-            SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}')
-            WHERE ?? is not null;`,
+          `UPDATE ??
+            SET ?? = (SELECT JSON_QUERY(T.*) FROM (SELECT ?? AS value, ? AS lastModifiedBy, ? AS lastModifiedTime, ? AS isStale) AS T)
+            WHERE ?? IS NOT NULL;`,

Ensure that all variables are properly parameterized to prevent injection.

packages/nocodb/src/db/sortV2.ts (1)

203-221: Remove duplicate condition check

The condition isAIPromptCol(column) is checked twice unnecessarily, leading to redundant code.

Apply this diff to remove the duplicate condition:

case UITypes.LongText: {
  if (isAIPromptCol(column)) {
    let col;
-   if (isAIPromptCol(column)) {
      if (knex.clientType() === 'pg') {
        col = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [
          column.column_name,
        ]);
      } else if (knex.clientType().startsWith('mysql')) {
        col = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [
          column.column_name,
        ]);
      } else if (knex.clientType() === 'sqlite3') {
        col = knex.raw(`json_extract(??, '$.value')`, [
          column.column_name,
        ]);
      } else if (knex.clientType() === 'mssql') {
        col = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
      }
-   }
    qb.orderBy(col, sort.direction || 'asc', nulls);
  } else {
    qb.orderBy(
      sanitize(column.column_name),
      sort.direction || 'asc',
      nulls,
    );
  }
  break;
}
packages/nc-gui/utils/columnUtils.ts (1)

269-309: Refactor to eliminate duplicate AI validation logic

The AI integration validation logic is duplicated in the Button and LongText cases within the isColumnInvalid function. Refactoring this code will improve maintainability and reduce potential errors.

Consider extracting the common logic into a helper function, such as isAiColumnInvalid, and reuse it in both cases:

function isAiColumnInvalid(
  colOptions: ButtonType,
  aiIntegrations: Partial<IntegrationType>[],
  isReadOnly: boolean,
): { isInvalid: boolean; tooltip: string } {
  const isInvalid =
    !colOptions.fk_integration_id ||
    (!isReadOnly &&
      !!colOptions.fk_integration_id &&
      !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'));
  const tooltip = 'title.aiIntegrationMissing';
  return { isInvalid, tooltip };
}

Then update the Button and LongText cases:

      } else if (colOptions.type === ButtonActionsType.Ai) {
-        result.isInvalid =
-          !colOptions.fk_integration_id ||
-          (isReadOnly
-            ? false
-            : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-        result.tooltip = 'title.aiIntegrationMissing'
+        Object.assign(
+          result,
+          isAiColumnInvalid(colOptions, aiIntegrations, isReadOnly),
+        );
      }

And similarly for the LongText case.

packages/nc-gui/components/smartsheet/expanded-form/index.vue (1)

330-333: LGTM! Good user feedback implementation.

The addition of a 5-second delay after copying the URL provides clear visual feedback to users, improving the user experience.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (4)

186-196: LGTM! Good validation for AI prompt columns.

The addition of hideAiPromptOnEdit condition correctly prevents AI prompt columns from being shown in edit mode, maintaining data integrity.


Line range hint 249-267: LGTM! Clean implementation of AI prompt type handling.

The onSelectType function has been enhanced to handle AI prompt columns by setting appropriate defaults and metadata.


600-602: LGTM! Clear and focused implementation.

The isAiPromptSelectOption function provides a clean way to identify AI prompt columns using the SDK utility.


1137-1141: LGTM! Good integration of AI prompt options.

The SmartsheetColumnLongTextOptions component is properly configured with AI prompt options and navigation functionality.

packages/nocodb/src/db/conditionV2.ts (1)

596-612: LGTM! Robust database-agnostic implementation for AI prompt columns.

The implementation correctly handles JSON value extraction for AI prompt columns across different database types:

  • PostgreSQL using jsonb->>'value'
  • MySQL using JSON_EXTRACT and JSON_UNQUOTE
  • SQLite using json_extract
  • MSSQL using JSON_VALUE

This ensures consistent behavior across different database backends.

packages/nc-gui/components/smartsheet/grid/Table.vue (2)

855-856: Improved AI column type detection logic

The code now properly handles both AI prompt columns and AI button types, making the implementation more robust and maintainable.


2140-2146: Enhanced expand button UI with proper styling

The expand button now has:

  • Proper border and hover states
  • Consistent icon sizing
  • Better accessibility with semantic markup
packages/nocodb/src/services/columns.service.ts (2)

71-74: Added data conversion utilities for AI columns

Clean import of utility functions for converting between AI record types and values.


2282-2304: Comprehensive AI prompt handling during column creation

The code properly:

  1. Validates prompt format
  2. Processes column references
  3. Ensures data consistency

Let's verify the column reference handling:

✅ Verification successful

Based on the search results, I can now verify the AI prompt handling. The code shows:

  1. The prompt handling is implemented in multiple places with proper validation
  2. The column reference replacement is consistently implemented using regex pattern /{(.*?)}/g
  3. There's proper error handling with NcError.badRequest when referenced fields are not found
  4. The prompt_raw and processed prompt are stored separately in the database schema
  5. The implementation is used consistently across the codebase

Let me verify one final aspect - the error handling implementation:


Based on the search results, I can now provide a complete verification of the AI prompt handling implementation. The code shows:

Comprehensive and secure AI prompt handling verified

The implementation is robust and secure because:

  1. It properly validates column references using regex pattern /{(.*?)}/g
  2. It includes proper error handling with NcError.badRequest when referenced fields are not found
  3. The prompt handling is consistently implemented across the codebase
  4. It maintains separation between raw prompt (prompt_raw) and processed prompt for security
  5. The column reference substitution is done after validating column existence
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential column reference issues in prompts

# Look for column references in prompts that might be invalid
ast-grep --pattern 'prompt_raw.replace(/{(.*?)}/g, $_)'

Length of output: 57


Script:

#!/bin/bash
# Let's try a different approach to verify AI prompt handling

# Search for AI prompt column handling
rg -A 10 "isAIPromptCol" 

# Search for prompt validation and processing
rg -A 5 "prompt_raw"

# Look for any error handling related to prompts
rg "NcError.badRequest.*prompt"

Length of output: 42415


Script:

#!/bin/bash
# Check the error handling implementation for AI prompts
ast-grep --pattern 'NcError.badRequest(`Field ${$_} not found`)'

# Look for any other prompt-related error handling
rg "throw.*prompt" -A 2

Length of output: 65629

packages/nocodb/src/db/BaseModelSqlv2.ts (3)

5721-5723: LGTM: Proper async handling with Promise.all

The change correctly handles the async nature of prepareNocoData by using Promise.all with map.


10071-10075: ⚠️ Potential issue

Correct the type of isStale to boolean

The isStale property is used as a boolean in the code. Currently, it's typed as string?, which can lead to type mismatches. It should be typed as boolean? to reflect its usage.

Apply this diff to fix the type:

const obj: {
  value?: string;
  lastModifiedBy?: string;
  lastModifiedTime?: string;
- isStale?: string;
+ isStale?: boolean;
} = {};

Likely invalid or redundant comment.


8635-8653: 🛠️ Refactor suggestion

Improve error handling for JSON parsing

The JSON parsing logic has minimal error handling with empty catch blocks that could hide important errors. Additionally, there's no validation of the parsed data structure.

Apply this diff to improve error handling:

if (d) {
  for (const col of jsonColumns) {
    if (d[col.id] && typeof d[col.id] === 'string') {
      try {
        d[col.id] = JSON.parse(d[col.id]);
+       // Validate expected data structure
+       if (typeof d[col.id] !== 'object') {
+         throw new Error('Invalid JSON structure');
+       }
-     } catch {}
+     } catch (e) {
+       logger.warn(`Error parsing JSON for column ${col.id}: ${e.message}`);
+       d[col.id] = null; // Set to null on parse error
+     }
    }

    if (d[col.id]?.length) {
      for (let i = 0; i < d[col.id].length; i++) {
        if (typeof d[col.id][i] === 'string') {
          try {
            d[col.id][i] = JSON.parse(d[col.id][i]);
+           // Validate array element structure
+           if (typeof d[col.id][i] !== 'object') {
+             throw new Error('Invalid JSON array element');
+           }
-         } catch {}
+         } catch (e) {
+           logger.warn(`Error parsing JSON array element: ${e.message}`);
+           d[col.id][i] = null;
+         }
        }
      }
    }
  }
}

Likely invalid or redundant comment.

Comment on lines 17 to 18
const meta = inject(MetaInj, ref())

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a default value for meta injection

The injected meta dependency uses ref() as a fallback, which could lead to runtime errors when accessing properties. Consider providing a more specific default value.

-const meta = inject(MetaInj, ref())
+const meta = inject(MetaInj, ref({ columns: [] }))
📝 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.

Suggested change
const meta = inject(MetaInj, ref())
const meta = inject(MetaInj, ref({ columns: [] }))

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch from 68ce927 to f412ec4 Compare December 7, 2024 08:10
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (14)
packages/nc-gui/components/smartsheet/expanded-form/index.vue (1)

330-333: Consider adding error handling to the copy operation.

The implementation of the URL copy feedback is good, providing clear user feedback for 5 seconds. However, the copy operation could fail.

Consider wrapping the copy operation in a try-catch block:

 const copyRecordUrl = async () => {
-  await copy(
-    encodeURI(
-      `${dashboardUrl?.value}#/${route.params.typeOrId}/${route.params.baseId}/${meta.value?.id}${
-        props.view ? `/${props.view.title}` : ''
-      }?rowId=${primaryKey.value}`,
-    ),
-  )
-
-  isRecordLinkCopied.value = true
-
-  await ncDelay(5000)
-
-  isRecordLinkCopied.value = false
+  try {
+    await copy(
+      encodeURI(
+        `${dashboardUrl?.value}#/${route.params.typeOrId}/${route.params.baseId}/${meta.value?.id}${
+          props.view ? `/${props.view.title}` : ''
+        }?rowId=${primaryKey.value}`,
+      ),
+    )
+    isRecordLinkCopied.value = true
+    await ncDelay(5000)
+    isRecordLinkCopied.value = false
+  } catch (error) {
+    message.error(t('msg.error.recordURLCopyFailed'))
+  }
 }
packages/nc-gui/utils/columnUtils.ts (1)

269-318: LGTM with suggestions: Function changes are well-structured

The changes to isColumnInvalid improve error handling by including tooltips and properly handling AI integrations. However, consider these improvements:

  1. Extract the AI validation logic into a helper function to reduce duplication
  2. Remove the redundant Formula type check

Consider extracting the AI validation into a helper function:

const isAiColumnInvalid = (
  colOptions: ButtonType,
  isReadOnly: boolean,
  aiIntegrations: Partial<IntegrationType>[]
): { isInvalid: boolean; tooltip: string } => ({
  isInvalid:
    !colOptions.fk_integration_id ||
    (isReadOnly
      ? false
      : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')),
  tooltip: 'title.aiIntegrationMissing'
})

Then use it in both Button and LongText cases:

case UITypes.Button: {
  const colOptions = col.colOptions as ButtonType
  if (colOptions.type === ButtonActionsType.Webhook) {
    result.isInvalid = !colOptions.fk_webhook_id
  } else if (colOptions.type === ButtonActionsType.Url) {
    result.isInvalid = !!colOptions.error
  } else if (colOptions.type === ButtonActionsType.Ai) {
    return isAiColumnInvalid(colOptions, isReadOnly, aiIntegrations)
  }
  break
}
case UITypes.LongText: {
  if (parseProp(col.meta)[LongTextAiMetaProp]) {
    return isAiColumnInvalid(col.colOptions as ButtonType, isReadOnly, aiIntegrations)
  }
}
packages/nc-gui/components/cell/TextArea.vue (1)

Line range hint 491-543: Consider extracting complex class logic into computed properties

The class binding logic is quite complex and could be more maintainable if extracted into computed properties.

Example refactor:

+ const expandButtonClasses = computed(() => ({
+   'right-1': isForm,
+   'right-0': !isForm,
+   'top-0': isGrid && !isExpandedFormOpen && !isForm && !(!rowHeight || rowHeight === 1),
+   'top-1': !(isGrid && !isExpandedFormOpen && !isForm)
+ }))
+
+ const expandButtonStyle = computed(() => 
+   isGrid && !isExpandedFormOpen && !isForm && (!rowHeight || rowHeight === 1)
+     ? { top: '50%', transform: 'translateY(-50%)' }
+     : undefined
+ )

  <div
    class="!absolute !hidden nc-text-area-expand-btn group-hover:block z-3 flex items-center gap-1"
-   :class="{
-     'right-1': isForm,
-     'right-0': !isForm,
-     'top-0': isGrid && !isExpandedFormOpen && !isForm && !(!rowHeight || rowHeight === 1),
-     'top-1': !(isGrid && !isExpandedFormOpen && !isForm)
-   }"
-   :style="
-     isGrid && !isExpandedFormOpen && !isForm && (!rowHeight || rowHeight === 1)
-       ? { top: '50%', transform: 'translateY(-50%)' }
-       : undefined
-   "
+   :class="expandButtonClasses"
+   :style="expandButtonStyle"
  >
packages/nc-gui/components/smartsheet/Kanban.vue (3)

Line range hint 1-1200: Consider splitting the component for better maintainability.

The Kanban component currently handles multiple responsibilities including drag-and-drop, CRUD operations, and UI state management. Consider breaking it down into smaller, more focused components:

  1. KanbanStack: Handle stack-level operations
  2. KanbanCard: Handle card-level operations
  3. KanbanDragDrop: Handle drag-and-drop logic

This separation would improve:

  • Code maintainability
  • Testing capabilities
  • Reusability
  • Performance through better component lifecycle management

Line range hint 89-103: Optimize infinite scroll performance.

The current infinite scroll implementation could be improved:

  1. Consider using virtual scrolling for large lists to reduce DOM elements.
  2. The debounced scroll handler could be optimized by:
    • Using IntersectionObserver instead of scroll events
    • Adding early return conditions
    • Implementing proper cleanup in component unmount

Example implementation using IntersectionObserver:

-const kanbanListScrollHandler = useDebounceFn(async (e: any) => {
-  if (!e.target) return
-
-  if (e.target.scrollTop + e.target.clientHeight + INFINITY_SCROLL_THRESHOLD >= e.target.scrollHeight) {
-    const stackTitle = e.target.getAttribute('data-stack-title')
+const setupInfiniteScroll = (element: HTMLElement) => {
+  const observer = new IntersectionObserver(
+    async (entries) => {
+      const entry = entries[0]
+      if (!entry.isIntersecting) return
+      
+      const stackTitle = element.getAttribute('data-stack-title')
+      if (!stackTitle) return
+
+      const stack = formattedData.value.get(stackTitle)
+      if (!stack) return
+
+      const pageSize = appInfo.value.defaultLimit || 25
+      if (stack.length >= (countByStack.value.get(stackTitle) ?? 0)) return
+
+      const page = Math.ceil(stack.length / pageSize)
+      await loadMoreKanbanData(stackTitle, {
+        offset: page * pageSize,
+      })
+    },
+    { threshold: 0.5 }
+  )
+
+  const sentinel = document.createElement('div')
+  element.appendChild(sentinel)
+  observer.observe(sentinel)
+
+  return () => {
+    observer.disconnect()
+    sentinel.remove()
+  }
+}

Line range hint 1-1200: Enhance accessibility support.

The component could benefit from improved accessibility:

  1. Add ARIA attributes for drag-and-drop operations:

    • aria-grabbed
    • aria-dropeffect
    • aria-describedby
  2. Improve keyboard navigation:

    • Add keyboard shortcuts for common operations
    • Implement focus management for drag-and-drop
    • Add skip links for large stacks
  3. Enhance screen reader support:

    • Add live regions for dynamic updates
    • Improve announcement of stack changes

Example implementation:

+const KEYBOARD_SHORTCUTS = {
+  MOVE_CARD: 'm',
+  EXPAND_CARD: 'e',
+  DELETE_CARD: 'd',
+}
+
+const setupKeyboardNavigation = () => {
+  const handleKeyDown = (e: KeyboardEvent) => {
+    if (e.key === KEYBOARD_SHORTCUTS.MOVE_CARD && e.ctrlKey) {
+      // Implement card movement logic
+    }
+  }
+
+  window.addEventListener('keydown', handleKeyDown)
+  return () => window.removeEventListener('keydown', handleKeyDown)
+}
packages/nc-gui/composables/useIntegrationsStore.ts (1)

Line range hint 447-496: Add error handling and loading state management

The loadDynamicIntegrations function needs improvements in several areas:

  1. Error handling for API call
  2. Loading state management
  3. Memory management for icon resources
const loadDynamicIntegrations = async () => {
+ isLoadingIntegrations.value = true
  if (integrationsInitialized.value) return

  integrationsInitialized.value = true

  try {
    const dynamicIntegrations = (await $api.integrations.list()) as {
      type: IntegrationsType
      subType: string
      meta: {
        title?: string
        icon?: string
        description?: string
        order?: number
      }
    }[]

    dynamicIntegrations.sort((a, b) => (a.meta.order ?? Infinity) - (b.meta.order ?? Infinity))

    for (const di of dynamicIntegrations) {
      let icon: FunctionalComponent<SVGAttributes, {}, any, {}> | VNode | undefined

      if (di.meta.icon) {
        if (di.meta.icon in iconMap) {
          icon = iconMap[di.meta.icon as keyof typeof iconMap]
        } else if (isValidURL(di.meta.icon)) {
          icon = h('img', {
            src: di.meta.icon,
            alt: di.meta.title || di.subType,
+           loading: 'lazy',
+           onerror: 'this.src="fallback-icon.png"'
          })
        }
      } else {
        icon = iconMap.puzzle
      }

      const integration: IntegrationItemType = {
        title: di.meta.title || di.subType,
        subType: di.subType,
        icon,
        type: di.type,
        isAvailable: true,
        dynamic: true,
      }

      allIntegrations.push(integration)
    }

    integrationsRefreshKey.value++
+  } catch (e) {
+    await message.error(await extractSdkResponseErrorMsg(e))
+  } finally {
+    isLoadingIntegrations.value = false
  }
}
packages/nc-gui/components/smartsheet/details/Fields.vue (2)

1040-1043: Consider refactoring field management logic for better maintainability

The field management logic, particularly in onFieldUpdate and saveChanges, is complex and handles multiple responsibilities. Consider breaking these down into smaller, more focused methods.

Some suggestions:

  1. Extract field validation logic into a separate method
  2. Split save operations by type (add/update/delete)
  3. Consider using a state management pattern for tracking field changes

Example refactor for the copy timeout:

+ const COPY_TIMEOUT_MS = 5000
+ 
+ async function resetCopyState() {
+   await delay(COPY_TIMEOUT_MS)
+   isFieldIdCopied.value = false
+ }
+
- await delay(5000)
- isFieldIdCopied.value = false
+ await resetCopyState()

Also applies to: 1041-1042, 1043-1043


1040-1043: Enhance AI integration error handling and state management

The AI integration could benefit from more robust error handling and centralized state management.

Consider:

  1. Creating an enum for AI error types
  2. Moving AI state management to a dedicated composable
  3. Adding retry logic for failed AI operations
  4. Implementing proper cleanup on component unmount

Example error handling enhancement:

enum AiErrorType {
  NO_INTEGRATION = 'no_integration',
  PREDICTION_FAILED = 'prediction_failed',
  RATE_LIMITED = 'rate_limited'
}

interface AiError {
  type: AiErrorType;
  message: string;
  retryable: boolean;
}
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

Line range hint 94-94: Use enum values instead of string literals for consistency

In the condition if (type === 'personal' && view.value.is_default), you are comparing type (which is of type LockType) to a string literal 'personal'. This might cause inconsistencies or bugs if the LockType enum changes.

Replace the string literal with the corresponding enum value:

-  if (type === 'personal' && view.value.is_default) {
+  if (type === LockType.Personal && view.value.is_default) {

This ensures type safety and consistency throughout the codebase.

packages/nocodb/src/services/columns.service.ts (1)

1601-1621: Add validation for prompt template format

The prompt template processing lacks validation for the template format. Consider adding checks for:

  • Valid template syntax
  • Maximum template length
  • Restricted characters/patterns
      let prompt = '';

      /*
        Substitute column alias with id in prompt
      */
      if (colBody.prompt_raw) {
+       // Validate prompt template format
+       const MAX_PROMPT_LENGTH = 1000;
+       if (colBody.prompt_raw.length > MAX_PROMPT_LENGTH) {
+         NcError.badRequest(`Prompt template exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`);
+       }
+       
+       // Check for valid template syntax
+       const templatePattern = /^[^{}]*(\{[^{}]+\}[^{}]*)*$/;
+       if (!templatePattern.test(colBody.prompt_raw)) {
+         NcError.badRequest('Invalid prompt template format');
+       }

        await table.getColumns(context);

        prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
          const column = table.columns.find((c) => c.title === p1);

          if (!column) {
            NcError.badRequest(`Field '${p1}' not found`);
          }

          return `{${column.id}}`;
        });
      }

      colBody.prompt = prompt;
packages/nocodb/src/db/conditionV2.ts (1)

596-612: Consider refactoring database-specific JSON handling

The JSON value extraction logic for AI prompt columns is duplicated across different database types. Consider refactoring this into a reusable helper function to improve maintainability.

+ // Add at the top of the file
+ function getAIPromptValueExtractor(knex: Knex, columnName: string) {
+   if (knex.clientType() === 'pg') {
+     return knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [columnName]);
+   } else if (knex.clientType().startsWith('mysql')) {
+     return knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [columnName]);
+   } else if (knex.clientType() === 'sqlite3') {
+     return knex.raw(`json_extract(??, '$.value')`, [columnName]);
+   } else if (knex.clientType() === 'mssql') {
+     return knex.raw(`JSON_VALUE(??, '$.value')`, [columnName]);
+   }
+   return null;
+ }

- if (isAIPromptCol(column)) {
-   if (knex.clientType() === 'pg') {
-     field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name]);
-   } else if (knex.clientType().startsWith('mysql')) {
-     field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name]);
-   } else if (knex.clientType() === 'sqlite3') {
-     field = knex.raw(`json_extract(??, '$.value')`, [column.column_name]);
-   } else if (knex.clientType() === 'mssql') {
-     field = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
-   }
+ if (isAIPromptCol(column)) {
+   field = getAIPromptValueExtractor(knex, column.column_name);
}
packages/nocodb/src/models/Column.ts (1)

Line range hint 1282-1286: Extract workspace validation into a shared helper

The workspace ID validation logic is duplicated between button and AI prompt columns. Consider extracting this into a shared helper function.

+ function resetWorkspaceSpecificProps(base: Base, options: any) {
+   if (base.fk_workspace_id !== options.fk_workspace_id) {
+     options.fk_workspace_id = null;
+     options.fk_integration_id = null;
+     options.model = null;
+   }
+ }

- if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
-   colOptions.fk_workspace_id = null;
-   colOptions.fk_integration_id = null;
-   colOptions.model = null;
- }
+ resetWorkspaceSpecificProps(base, colOptions);
packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (1)

182-182: Consider preserving AI prompt column metadata during import

While filtering out AI prompt columns from the initial import is correct, consider preserving their metadata for reference or validation.

+ // Track filtered AI prompt columns for validation
+ const filteredAIPromptColumns = [];

const reducedColumnSet = modelData.columns.filter(
  (a) => {
    const keep = !isVirtualCol(a) &&
      a.uidt !== UITypes.ForeignKey &&
-     !isAIPromptCol(a) &&
+     !(isAIPromptCol(a) && (filteredAIPromptColumns.push(a), true)) &&
      (param.importColumnIds
        ? param.importColumnIds.includes(getEntityIdentifier(a.id))
        : true);
    return keep;
  }
);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 68ce927 and f412ec4.

📒 Files selected for processing (69)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (46)
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nocodb/src/models/index.ts
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/components/nc/Button.vue
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/general/Loader.vue
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nocodb/src/db/sortV2.ts
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nocodb/src/models/Integration.ts
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/ai/Settings.vue
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/components/smartsheet/grid/Table.vue
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (31)
packages/nc-gui/utils/columnUtils.ts (4)

2-3: LGTM: New imports and constants are well-organized

The new imports and constants are properly structured and follow the existing naming conventions.

Also applies to: 15-18


27-32: LGTM: New AIPrompt UI type is properly defined

The new AIPrompt UI type follows the existing pattern and includes appropriate flags.


315-317: Remove redundant Formula type validation

The Formula type validation is duplicated. It exists both in the switch statement and after it.


289-309: Extract duplicated AI validation logic

The AI integration validation logic is duplicated between Button and LongText types. Consider extracting it into a helper function:

packages/nc-gui/components/cell/TextArea.vue (6)

17-18: Add type safety to meta injection

The injected meta dependency uses ref() as a fallback, which could lead to runtime errors when accessing properties.


100-103: Add null checks to prevent runtime errors

The use of non-null assertions ! in meta.value!.columns! may lead to runtime errors if meta.value or columns are undefined.


Line range hint 408-466: LGTM! Well-structured AI warning and regeneration UI

The implementation properly handles:

  • Conditional rendering based on AI states
  • Clear warning messages for stale data
  • User-friendly regeneration controls

629-630: Correct the usage of Tailwind CSS classes

The class animate-infinite is not a valid Tailwind CSS utility.


Line range hint 637-648: LGTM! Clear and well-structured warning message

The warning message implementation:

  • Uses appropriate icons and styling
  • Provides clear explanation of the stale state
  • Maintains consistent design language

Line range hint 708-724: LGTM! Well-structured styles

The CSS implementation:

  • Properly scopes styles
  • Uses appropriate selectors
  • Maintains consistent naming conventions
packages/nc-gui/components/smartsheet/Kanban.vue (1)

1146-1146: Icon change aligns with design system consistency.

The change from iconMap.expand to iconMap.maximize for the expand record action maintains consistency with similar icon usage in other components like Gallery.vue, attachment/index.vue, and ManyToMany.vue.

packages/nc-gui/composables/useIntegrationsStore.ts (2)

229-238: Avoid mutating objects within map—return new objects instead

The current implementation mutates the object within the map function. This can lead to unexpected behavior in reactive systems.

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.title = integration.title
-     return i
+     return { ...i, title: integration.title }
    }
    return i
  })
}

258-269: Avoid mutating objects within map—return new objects instead

Similar to the previous comment, mutate objects immutably within the map function.

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.is_default = true
+     return { ...i, is_default: true }
    } else {
-     i.is_default = false
+     return { ...i, is_default: false }
    }
-     return i
  })
}
packages/nocodb/src/db/BaseModelSqlv2.ts (4)

10062-10137: LGTM! AI prompt handling implementation

The implementation for handling AI prompt columns is well structured and includes:

  • Proper metadata tracking (lastModifiedBy, lastModifiedTime)
  • Stale state management
  • System vs user modification handling
  • Referenced column change detection

10071-10075: 🛠️ Refactor suggestion

Correct the type of isStale to boolean

The isStale property is used as a boolean in the code. Currently, it's typed as string?, which can lead to type mismatches. It should be typed as boolean? to reflect its usage.

Apply this diff to fix the type:

const obj: {
  value?: string;
  lastModifiedBy?: string;
  lastModifiedTime?: string;
- isStale?: string;
+ isStale?: boolean;
} = {};

Likely invalid or redundant comment.


5721-5723: ⚠️ Potential issue

Ensure prepareNocoData is properly awaited inside map

The prepareNocoData function is asynchronous, but its calls inside insertDatas.map are not being properly awaited. This can lead to unhandled Promise rejections and unexpected behavior.

Apply this diff to fix the issue:

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.


8635-8653: 🛠️ Refactor suggestion

Improve error handling for JSON parsing

The JSON parsing logic has minimal error handling with empty catch blocks that could hide important errors. Additionally, there's no validation of the parsed data structure.

Apply this diff to improve error handling:

if (d) {
  for (const col of jsonColumns) {
    if (d[col.id] && typeof d[col.id] === 'string') {
      try {
        d[col.id] = JSON.parse(d[col.id]);
+       // Validate expected data structure
+       if (typeof d[col.id] !== 'object') {
+         throw new Error('Invalid JSON structure');
+       }
-     } catch {}
+     } catch (e) {
+       logger.warn(`Error parsing JSON for column ${col.id}: ${e.message}`);
+       d[col.id] = null; // Set to null on parse error
+     }
    }

    if (d[col.id]?.length) {
      for (let i = 0; i < d[col.id].length; i++) {
        if (typeof d[col.id][i] === 'string') {
          try {
            d[col.id][i] = JSON.parse(d[col.id][i]);
+           // Validate array element structure
+           if (typeof d[col.id][i] !== 'object') {
+             throw new Error('Invalid JSON array element');
+           }
-         } catch {}
+         } catch (e) {
+           logger.warn(`Error parsing JSON array element: ${e.message}`);
+           d[col.id][i] = null;
+         }
        }
      }
    }
  }
}

Likely invalid or redundant comment.

packages/nc-gui/components/cell/AI.vue (3)

118-120: LGTM! Well-structured computed property

The isDisabledAiButton computed property effectively consolidates multiple conditions into a single, readable expression.


131-139: ⚠️ Potential issue

Fix tooltip disabled logic

The tooltip's disabled condition appears to be inverted, potentially showing tooltips when they should be hidden.

-    <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+    <NcTooltip :disabled="!isDisabledAiButton" class="flex">

Likely invalid or redundant comment.


81-92: 🛠️ Refactor suggestion

Improve error handling for AI response processing

The object processing logic lacks proper error handling for malformed responses.

 if (obj && typeof obj === 'object') {
+  try {
     vModel.value = obj
     setTimeout(() => {
       isAiEdited.value = false
     }, 100)
+  } catch (error) {
+    console.error('Error processing AI response:', error)
+    message.error('Failed to process AI response')
+  }
 } else {
+  try {
     vModel.value = {
       ...(ncIsObject(vModel.value) ? vModel.value : {}),
       isStale: false,
       value: resRow[column.value.title!],
     }
+  } catch (error) {
+    console.error('Error setting AI response value:', error)
+    message.error('Failed to set AI response value')
+  }
 }

Likely invalid or redundant comment.

packages/nc-gui/components/ai/PromptWithFields.vue (2)

63-68: LGTM! Improved suggestion filtering

The addition of exact match condition ${o.title?.toLowerCase()}} === query.toLowerCase()` enhances the accuracy of suggestions.


159-174: ⚠️ Potential issue

Add tooltip cleanup on component unmount

The tooltips are initialized but not cleaned up, which could lead to memory leaks.

+const tooltipInstances: any[] = []

 function loadMentionFieldTagTooltip() {
   document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
     const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
     if (!tooltip || el.scrollWidth <= el.clientWidth) return
-    tippy(el, {
+    const instance = tippy(el, {
       content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
       placement: 'top',
       allowHTML: true,
       arrow: true,
       animation: 'fade',
       duration: 0,
       maxWidth: '200px',
     })
+    tooltipInstances.push(instance)
   })
 }

+onBeforeUnmount(() => {
+  tooltipInstances.forEach(instance => instance.destroy())
+  tooltipInstances.length = 0
+})

Likely invalid or redundant comment.

packages/nc-gui/composables/useNocoAi.ts (2)

20-21: LGTM! Type enhancement for better flexibility

The change from IntegrationType[] to Partial<IntegrationType>[] allows for more flexible integration definitions.


24-28: ⚠️ Potential issue

Fix the condition in isAiIntegrationAvailableInList

The function is using the computed property name instead of its value.

 const isAiIntegrationAvailableInList = (integrationId?: string) => {
-  if (!aiIntegrationAvailable) return false
+  if (!aiIntegrationAvailable.value) return false

   return ncIsArrayIncludes(aiIntegrations.value, integrationId, 'id')
 }

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/details/Fields.vue (1)

1040-1043: ⚠️ Potential issue

Replace undefined ncDelay with delay from @vueuse/core

The ncDelay function is undefined and will cause runtime errors.

Apply this diff to fix the issue:

-  await ncDelay(5000)
+  await delay(5000)
packages/nocodb/src/models/AIColumn.ts (2)

26-26: Avoid using 'this' in static methods

Using this inside static methods can be confusing and may lead to unexpected behavior. It's clearer and more explicit to reference the class name directly.

Apply this diff to fix the issue:

-        return this._insert(
+        return AIColumn._insert(
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: Avoid using 'this' in static methods

Using this inside static methods can be confusing and may lead to unexpected behavior. It's clearer and more explicit to reference the class name directly.

Apply this diff to fix the issue:

-        return this._update(
+        return AIColumn._update(
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: Avoid using 'this' in static methods

Using this inside static methods can be confusing and may lead to unexpected behavior. It's clearer and more explicit to reference the class name directly.

Apply this diff to fix the issue:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: Avoid using 'this' in static methods

Using this inside static methods can be confusing and may lead to unexpected behavior. It's clearer and more explicit to reference the class name directly.

Apply this diff to fix the issue:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

136-149: Prevent SQL injection by avoiding string concatenation in SQL queries

In the MSSQL query, string concatenation is used within the JSON_QUERY function to build a JSON string. This can lead to SQL injection vulnerabilities if any of the concatenated values are user-controlled or not properly sanitized. It's important to use parameterized queries to prevent such risks.

Consider refactoring the query to eliminate string concatenation. You can construct the JSON object using parameter bindings or MSSQL's JSON functions more safely. For example:

-        `UPDATE ??
-          SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}')
-          WHERE ?? is not null;`,
+        `UPDATE ??
+          SET ?? = (SELECT TOP 1 *
+                    FROM (VALUES (??, ?, ?, ?)) AS t(value, lastModifiedBy, lastModifiedTime, isStale)
+                    FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
+          WHERE ?? IS NOT NULL;`,

Alternatively, you can use JSON_MODIFY or other JSON functions to safely construct the JSON object without concatenation.

packages/nocodb/src/models/Column.ts (1)

921-924: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code uses a hardcoded {column_id} placeholder pattern which is incorrect. It should check for the actual column ID.

Apply this diff to fix the placeholder matching:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines 1622 to 1645
// If column wasn't AI before, convert the data to AIRecordType format
if (
column.uidt !== UITypes.LongText ||
column.meta?.[LongTextAiMetaProp] !== true
) {
const baseModel = await reuseOrSave('baseModel', reuse, async () =>
Model.getBaseModelSQL(context, {
id: table.id,
dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
NcConnectionMgrv2.get(source),
),
}),
);

await convertValueToAIRecordType({
source,
table,
column,
baseModel,
sqlClient,
user: param.user,
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation and error handling for data conversion

The data conversion from non-AI to AI format lacks proper validation and error handling. Consider adding checks for data integrity and handling conversion failures gracefully.

        // If column wasn't AI before, convert the data to AIRecordType format
        if (
          column.uidt !== UITypes.LongText ||
          column.meta?.[LongTextAiMetaProp] !== true
        ) {
          const baseModel = await reuseOrSave('baseModel', reuse, async () =>
            Model.getBaseModelSQL(context, {
              id: table.id,
              dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
                NcConnectionMgrv2.get(source),
              ),
            }),
          );

+         // Start transaction
+         const trx = await sqlClient.knex.transaction();
+         try {
+           // Validate data before conversion
+           const validationResult = await baseModel.validateData({
+             column,
+             user: param.user
+           });
+           
+           if (!validationResult.valid) {
+             throw new Error(`Data validation failed: ${validationResult.message}`);
+           }
+
            await convertValueToAIRecordType({
              source,
              table,
              column,
              baseModel,
              sqlClient,
              user: param.user,
            });
+           
+           await trx.commit();
+         } catch (error) {
+           await trx.rollback();
+           NcError.badRequest(`Failed to convert to AI format: ${error.message}`);
+         }
        }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 2282 to 2304
if (isAIPromptCol(colBody)) {
let prompt = '';

/*
Substitute column alias with id in prompt
*/
if (colBody.prompt_raw) {
await table.getColumns(context);

prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
const column = table.columns.find((c) => c.title === p1);

if (!column) {
NcError.badRequest(`Field '${p1}' not found`);
}

return `{${column.id}}`;
});
}

colBody.prompt = prompt;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor duplicate prompt processing logic and add validations

The prompt processing logic is duplicated between column update and add operations. Additionally, there are missing validations for AI integration requirements.

  1. Extract common prompt processing logic to a shared method:
+     private async processPromptTemplate(
+       context: NcContext,
+       table: Model,
+       promptRaw: string
+     ): Promise<string> {
+       await table.getColumns(context);
+       
+       // Validate prompt template format
+       const MAX_PROMPT_LENGTH = 1000;
+       if (promptRaw.length > MAX_PROMPT_LENGTH) {
+         NcError.badRequest(`Prompt template exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`);
+       }
+       
+       // Check for valid template syntax
+       const templatePattern = /^[^{}]*(\{[^{}]+\}[^{}]*)*$/;
+       if (!templatePattern.test(promptRaw)) {
+         NcError.badRequest('Invalid prompt template format');
+       }
+
+       return promptRaw.replace(/{(.*?)}/g, (match, p1) => {
+         const column = table.columns.find((c) => c.title === p1);
+         if (!column) {
+           NcError.badRequest(`Field '${p1}' not found`);
+         }
+         return `{${column.id}}`;
+       });
+     }

      if (isAIPromptCol(colBody)) {
        let prompt = '';

        if (colBody.prompt_raw) {
-         await table.getColumns(context);
-         prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
-           const column = table.columns.find((c) => c.title === p1);
-           if (!column) {
-             NcError.badRequest(`Field '${p1}' not found`);
-           }
-           return `{${column.id}}`;
-         });
+         prompt = await this.processPromptTemplate(context, table, colBody.prompt_raw);
        }

        colBody.prompt = prompt;
      }
  1. Add validation for AI integration requirements:
      if (isAIPromptCol(colBody)) {
+       // Validate AI integration requirements
+       if (!colBody.meta?.aiEnabled) {
+         NcError.badRequest('AI integration must be enabled for AI prompt columns');
+       }
+
+       // Validate AI model configuration
+       if (!colBody.meta?.aiModel) {
+         NcError.badRequest('AI model must be specified for AI prompt columns');
+       }

        let prompt = '';
        // ... rest of the code
      }
  1. Add JSDoc documentation for the AI prompt handling:
+     /**
+      * Processes an AI prompt column during creation
+      * @param colBody - The column configuration
+      * @param table - The table model
+      * @throws {NcError} When AI integration requirements are not met
+      * @throws {NcError} When prompt template is invalid
+      */
      if (isAIPromptCol(colBody)) {
        // ... rest of the code
      }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 1578 to 1600
if (
isAIPromptCol(column) &&
(colBody.uidt !== UITypes.LongText ||
(colBody.uidt === UITypes.LongText &&
colBody.meta?.[LongTextAiMetaProp] !== true))
) {
const baseModel = await reuseOrSave('baseModel', reuse, async () =>
Model.getBaseModelSQL(context, {
id: table.id,
dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
NcConnectionMgrv2.get(source),
),
}),
);

await convertAIRecordTypeToValue({
source,
table,
column,
baseModel,
sqlClient,
});
} else if (isAIPromptCol(colBody)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and transaction management for type conversion

The conversion between AI and non-AI column types involves data transformation that should be wrapped in a transaction to maintain data consistency. Additionally, error handling should be added for data conversion failures.

      if (
        isAIPromptCol(column) &&
        (colBody.uidt !== UITypes.LongText ||
          (colBody.uidt === UITypes.LongText &&
            colBody.meta?.[LongTextAiMetaProp] !== true))
      ) {
        const baseModel = await reuseOrSave('baseModel', reuse, async () =>
          Model.getBaseModelSQL(context, {
            id: table.id,
            dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
              NcConnectionMgrv2.get(source),
            ),
          }),
        );

+       // Start transaction
+       const trx = await sqlClient.knex.transaction();
+       try {
          await convertAIRecordTypeToValue({
            source,
            table,
            column,
            baseModel,
            sqlClient,
          });
+         await trx.commit();
+       } catch (error) {
+         await trx.rollback();
+         NcError.badRequest(`Failed to convert AI column: ${error.message}`);
+       }
      }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 452 to 469
case UITypes.LongText: {
if (column.meta?.[LongTextAiMetaProp] === true) {
await AIColumn.insert(
context,
{
fk_model_id: column.fk_model_id,
fk_column_id: colId,
fk_integration_id: column.fk_integration_id,
model: column.model,
prompt: column.prompt,
prompt_raw: column.prompt_raw,
error: column.error,
},
ncMeta,
);
}
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for AI column insertion

The AI column insertion lacks error handling. Consider adding try-catch and logging.

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   try {
      await AIColumn.insert(
        context,
        {
          fk_model_id: column.fk_model_id,
          fk_column_id: colId,
          fk_integration_id: column.fk_integration_id,
          model: column.model,
          prompt: column.prompt,
          prompt_raw: column.prompt_raw,
          error: column.error,
        },
        ncMeta,
      );
+   } catch (error) {
+     logger.error(`Failed to insert AI column: ${error.message}`);
+     throw error;
+   }
  }
  break;
}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 1281 to 1253
} else if (isAIPromptCol(col)) {
if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
colOptions.fk_workspace_id = null;
colOptions.fk_integration_id = null;
colOptions.model = null;
}

const freshModelData = await this.columnsService.columnAdd(context, {
tableId: getIdOrExternalId(getParentIdentifier(col.id)),
column: withoutId({
...flatCol,
...{
fk_integration_id: colOptions.fk_integration_id,
model: colOptions.model,
prompt_raw: colOptions.prompt_raw,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation and error handling for AI prompt column import

The AI prompt column import lacks validation and error handling. Consider adding checks for required fields and error handling.

} else if (isAIPromptCol(col)) {
  if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
    colOptions.fk_workspace_id = null;
    colOptions.fk_integration_id = null;
    colOptions.model = null;
  }

+ // Validate required fields
+ if (!colOptions.prompt_raw) {
+   this.logger.warn(`Skipping AI prompt column import: Missing prompt_raw for column ${col.title}`);
+   continue;
+ }

+ try {
    const freshModelData = await this.columnsService.columnAdd(context, {
      tableId: getIdOrExternalId(getParentIdentifier(col.id)),
      column: withoutId({
        ...flatCol,
        ...{
          fk_integration_id: colOptions.fk_integration_id,
          model: colOptions.model,
          prompt_raw: colOptions.prompt_raw,
        },
      }) as any,
      req: param.req,
      user: param.user,
    });
+ } catch (error) {
+   this.logger.error(`Failed to import AI prompt column ${col.title}: ${error.message}`);
+   throw error;
+ }
📝 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.

Suggested change
8000
} else if (isAIPromptCol(col)) {
if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
colOptions.fk_workspace_id = null;
colOptions.fk_integration_id = null;
colOptions.model = null;
}
const freshModelData = await this.columnsService.columnAdd(context, {
tableId: getIdOrExternalId(getParentIdentifier(col.id)),
column: withoutId({
...flatCol,
...{
fk_integration_id: colOptions.fk_integration_id,
model: colOptions.model,
prompt_raw: colOptions.prompt_raw,
},
} else if (isAIPromptCol(col)) {
if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
colOptions.fk_workspace_id = null;
colOptions.fk_integration_id = null;
colOptions.model = null;
}
// Validate required fields
if (!colOptions.prompt_raw) {
this.logger.warn(`Skipping AI prompt column import: Missing prompt_raw for column ${col.title}`);
continue;
}
try {
const freshModelData = await this.columnsService.columnAdd(context, {
tableId: getIdOrExternalId(getParentIdentifier(col.id)),
column: withoutId({
...flatCol,
...{
fk_integration_id: colOptions.fk_integration_id,
model: colOptions.model,
prompt_raw: colOptions.prompt_raw,
},
}) as any,
req: param.req,
user: param.user,
});
} catch (error) {
this.logger.error(`Failed to import AI prompt column ${col.title}: ${error.message}`);
throw error;
}

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch from f412ec4 to 43ddfc6 Compare December 7, 2024 09:13
Copy link
Contributor
@coderabbitai coderabbitai bot left a 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

🧹 Outside diff range and nitpick comments (14)
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

156-156: Consider reducing the delay duration

5000ms (5 seconds) is an unusually long duration for copy feedback. Consider reducing it to 2000ms (2 seconds) which is more standard for temporary UI feedback.

-  await ncDelay(5000)
+  await ncDelay(2000)
packages/nc-gui/components/smartsheet/Kanban.vue (2)

Line range hint 249-282: Consider extracting infinite scroll logic into a reusable composable

The infinite scroll implementation could be moved to a separate composable to promote reusability across different views (Kanban, Gallery, etc.).

Example implementation:

// composables/useInfiniteScroll.ts
export function useInfiniteScroll(options: {
  threshold?: number
  onLoadMore: (e: Event) => Promise<void>
}) {
  const { threshold = 100, onLoadMore } = options
  
  const scrollHandler = useDebounceFn(async (e: Event) => {
    const target = e.target as HTMLElement
    if (!target) return
    
    if (target.scrollTop + target.clientHeight + threshold >= target.scrollHeight) {
      await onLoadMore(e)
    }
  })

  const scrollRef: VNodeRef = (element) => {
    if (element) {
      element.removeEventListener('scroll', scrollHandler)
      element.addEventListener('scroll', scrollHandler)
    }
  }

  return {
    scrollRef
  }
}

Line range hint 283-379: Consider extracting stack operations into a separate component

The stack operations (collapse/expand, rename, delete) could be moved to a dedicated component to improve maintainability and reduce the complexity of the main Kanban component.

Example structure:

<!-- components/smartsheet/KanbanStack.vue -->
<script setup lang="ts">
interface Props {
  stack: Stack
  index: number
  isCollapsed: boolean
  // ... other props
}

// ... emit stack operations
</script>

<template>
  <div class="nc-kanban-stack">
    <!-- Stack header with operations -->
    <!-- Stack content -->
  </div>
</template>
packages/nc-gui/components/cell/TextArea.vue (2)

Line range hint 421-466: Consider extracting the user display logic into a computed property

The repeated user display name logic could be simplified by extracting it into a computed property.

+const userDisplayName = computed(() => {
+  if (!props.aiMeta?.lastModifiedBy) return ''
+  return user?.id === props.aiMeta?.lastModifiedBy
+    ? 'you'
+    : idUserMap[props.aiMeta?.lastModifiedBy]?.display_name || idUserMap[props.aiMeta?.lastModifiedBy]?.email
+})

-user?.id === props.aiMeta?.lastModifiedBy
-  ? 'you'
-  : idUserMap[props.aiMeta?.lastModifiedBy]?.display_name || idUserMap[props.aiMeta?.lastModifiedBy]?.email
+userDisplayName.value

Line range hint 637-648: Consider using design tokens for consistent styling

The hardcoded color values and spacing in the AI warning message could be replaced with design tokens for better maintainability.

-<div class="flex items-center p-4 bg-nc-bg-purple-light gap-4">
+<div class="flex items-center p-4 bg-nc-bg-purple gap-4">
-  <GeneralIcon icon="alertTriangleSolid" class="text-nc-content-purple-medium h-6 w-6 flex-none" />
+  <GeneralIcon icon="alertTriangleSolid" class="text-nc-purple h-6 w-6 flex-none" />
   <div class="flex flex-col">
-    <div class="font-bold text-base text-nc-content-gray">Record Data Updated</div>
+    <div class="font-bold text-base text-nc-gray">Record Data Updated</div>
-    <div class="text-nc-content-gray-muted text-sm">
+    <div class="text-nc-gray-muted text-sm">
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (3)

Line range hint 4-16: Consider enhancing type safety and prop definitions.

The component's props could be more explicitly typed for better maintainability and type safety.

Consider defining an interface for the props:

+interface AiButtonOptionsProps {
+  value: any  // Consider being more specific about the type
+  submitBtnLabel: {
+    label: string
+    loadingLabel: string
+  }
+  saving: boolean
+}

-const props = defineProps<{
-  value: any
-  submitBtnLabel: {
-    label: string
-    loadingLabel: string
-  }
-  saving: boolean
-}>()
+const props = defineProps<AiButtonOptionsProps>()

471-475: Enhance tooltip accessibility.

The tooltip implementation could benefit from improved accessibility features.

Consider adding ARIA attributes and keyboard navigation:

 <NcTooltip show-on-truncate-only class="truncate max-w-[150px]">
   <template #title>{{ op.title }}</template>
-  {{ op.title }}
+  <span role="tooltip" tabindex="0" aria-label="{{ op.title }}">
+    {{ op.title }}
+  </span>
 </NcTooltip>

Line range hint 32-38: Consider adding input validation and sanitization.

The component processes user input and AI-generated content without explicit sanitization, which could potentially lead to XSS vulnerabilities.

Consider implementing input validation and sanitization:

  1. Add input validation for user-provided content
  2. Sanitize AI-generated content before rendering
  3. Implement Content Security Policy (CSP) headers

Example implementation:

const sanitizeContent = (content: string): string => {
  // Implement content sanitization logic
  return DOMPurify.sanitize(content)
}

// Use in template
<div v-html="sanitizeContent(aiGeneratedContent)"></div>
packages/nc-gui/composables/useIntegrationsStore.ts (1)

Consider adding error handling and extracting icon processing logic

The codebase shows consistent error handling patterns using try-catch blocks with extractSdkResponseErrorMsg and extractSdkResponseErrorMsgv2 utilities, but the loadDynamicIntegrations function lacks error handling. Consider:

  1. Adding error handling for the API call:
try {
  const dynamicIntegrations = await $api.integrations.list()
  // ... rest of the code
} catch (e) {
  await message.error(await extractSdkResponseErrorMsg(e))
  integrationsInitialized.value = false
}
  1. Extracting the icon processing logic into a separate function as suggested in the diff.

The refactoring will improve:

  • Error handling consistency with the rest of the codebase
  • Code maintainability by separating concerns
  • Error recovery by resetting the initialization flag on failure
🔗 Analysis chain

Line range hint 447-496: Consider splitting the dynamic integration loading logic

The loadDynamicIntegrations function handles multiple responsibilities. Consider splitting it into smaller, focused functions for better maintainability.

+ const processIntegrationIcon = (meta: { icon?: string, title?: string }, subType: string) => {
+   if (!meta.icon) return iconMap.puzzle
+   
+   if (meta.icon in iconMap) {
+     return iconMap[meta.icon as keyof typeof iconMap]
+   }
+   
+   if (isValidURL(meta.icon)) {
+     return h('img', {
+       src: meta.icon,
+       alt: meta.title || subType,
+     })
+   }
+   
+   return iconMap.puzzle
+ }

  const loadDynamicIntegrations = async () => {
    if (integrationsInitialized.value) return
    integrationsInitialized.value = true

    const dynamicIntegrations = (await $api.integrations.list()) as {
      type: IntegrationsType
      subType: string
      meta: {
        title?: string
        icon?: string
        description?: string
        order?: number
      }
    }[]

    dynamicIntegrations.sort((a, b) => (a.meta.order ?? Infinity) - (b.meta.order ?? Infinity))

    for (const di of dynamicIntegrations) {
-     let icon: FunctionalComponent<SVGAttributes, {}, any, {}> | VNode
-     if (di.meta.icon) {
-       if (di.meta.icon in iconMap) {
-         icon = iconMap[di.meta.icon as keyof typeof iconMap]
-       } else {
-         if (isValidURL(di.meta.icon)) {
-           icon = h('img', {
-             src: di.meta.icon,
-             alt: di.meta.title || di.subType,
-           })
-         }
-       }
-     } else {
-       icon = iconMap.puzzle
-     }
+     const icon = processIntegrationIcon(di.meta, di.subType)

      const integration: IntegrationItemType = {
        title: di.meta.title || di.subType,
        subType: di.subType,
        icon,
        type: di.type,
        isAvailable: true,
        dynamic: true,
      }

      allIntegrations.push(integration)
      integrationsRefreshKey.value++
    }
  }

Also, consider adding explicit error handling for the API call and icon loading:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing error handlers for integration loading
rg -A 2 "catch.*{" .

Length of output: 102


Script:

#!/bin/bash
# Check if there are any existing error handlers for integration 
10000
loading with fixed regex
rg -A 2 'catch\s*\{' packages/nc-gui/composables/useIntegrationsStore.ts

# Look for try-catch blocks in the file
rg -A 5 'try\s*\{' packages/nc-gui/composables/useIntegrationsStore.ts

# Check if there are any error handling utilities or patterns used
rg 'error|catch|throw' packages/nc-gui/composables/useIntegrationsStore.ts

Length of output: 3175

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (1)

Line range hint 1244-1280: Document workspace ID security check

The workspace ID validation and nullification is a good security measure. Consider adding a comment explaining this security check and its implications.

+ // Clear workspace and integration IDs if they don't match the base workspace
+ // This prevents unauthorized access across workspaces
 if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
   colOptions.fk_workspace_id = null;
   colOptions.fk_integration_id = null;
   colOptions.model = null;
 }
packages/nc-gui/components/ai/Settings.vue (1)

84-86: Consider adding error handling for model initialization

The condition if (!vModel.value || !availableModels.value.length) could be enhanced with error handling for cases where the API call fails.

-    if (!vModel.value || !availableModels.value.length) {
-      onIntegrationChange()
-    }
+    if (!vModel.value || !availableModels.value.length) {
+      try {
+        await onIntegrationChange()
+      } catch (error) {
+        console.error('Failed to initialize models:', error)
+        // Consider showing user feedback
+      }
+    }
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (2)

186-196: Improve readability of AI prompt filter condition

The AI prompt filter condition is mixed with other conditions making it harder to read. Consider extracting it into a separate variable for better readability.

+ const isAiPromptEditHidden = !((t.name === AIPrompt || t.name === AIButton) && isEdit.value)
  return (
    systemFiledNotEdited &&
    geoDataToggle &&
    !specificDBType &&
    showDeprecatedField &&
    isAllowToAddInFormView &&
-   hideAiPromptOnEdit
+   isAiPromptEditHidden
  )

1070-1076: Simplify nested ternary operators for icon selection

The nested ternary operators for icon selection reduce readability. Consider using a computed property or helper function.

+ const getColumnIcon = (optName: string, isOpen: boolean) => {
+   if (isAiButtonSelectOption(optName) && !isOpen) return iconMap.cellAiButton
+   if (isAiPromptSelectOption(optName) && !isOpen) return iconMap.cellAi
+   return opt.icon
+ }

- :is="isAiButtonSelectOption(opt.name) && !isColumnTypeOpen 
-   ? iconMap.cellAiButton 
-   : isAiPromptSelectOption(opt.name) && !isColumnTypeOpen
-   ? iconMap.cellAi
-   : opt.icon"
+ :is="getColumnIcon(opt.name, isColumnTypeOpen)"
packages/nocodb/src/models/Column.ts (1)

1957-1970: Optimize bulk insert for AI columns

The bulk insert implementation could be optimized by batching the inserts.

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   // Batch inserts in groups of 100 for better performance
+   const batchSize = 100;
+   for (let i = 0; i < insertArr.length; i += batchSize) {
+     const batch = insertArr.slice(i, i + batchSize);
      insertArr.push({
        fk_model_id: column.fk_model_id,
        fk_column_id: column.id,
        fk_integration_id: column.fk_integration_id,
        model: column.model,
        prompt: column.prompt,
        prompt_raw: column.prompt_raw,
        error: column.error,
      });
+   }
  }
  break;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f412ec4 and 43ddfc6.

📒 Files selected for processing (69)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (10 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (43)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nocodb/src/models/index.ts
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/components/general/Loader.vue
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nocodb/src/utils/dataConversion.ts
  • packages/nocodb/src/models/Integration.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/cell/attachment/index.vue
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (52)
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: ⚠️ Potential issue

Missing import for ncDelay function

The ncDelay function is used but not imported. This will cause a runtime error.

Import the function from the appropriate utility module:

<script lang="ts" setup>
+import { ncDelay } from '~/utils/ncDelay';
packages/nc-gui/components/smartsheet/Kanban.vue (1)

1146-1146: LGTM: Icon standardization

The change from expand to maximize icon aligns with similar updates in other components, maintaining consistency across the UI.

packages/nc-gui/components/cell/TextArea.vue (6)

17-18: Consider using a more specific default value for meta injection

The injected meta dependency uses an empty ref() as a fallback, which could lead to runtime errors when accessing properties.


100-103: Add null checks to prevent potential runtime errors

The use of non-null assertions (!) in meta.value!.columns! may lead to runtime errors if meta.value or columns are undefined.


104-111: LGTM: Well-structured computed property for AI generation status

The isAiGenerating computed property effectively combines multiple conditions to determine the AI generation state.


Line range hint 408-421: LGTM: Clear and informative AI warning message

The warning message implementation effectively communicates the stale state of AI-generated content and provides clear guidance for user action.


629-630: Correct the usage of Tailwind CSS classes

The class animate-infinite is not a valid Tailwind CSS utility. The animate-spin class already provides an infinite spin animation.


Line range hint 708-724: LGTM: Well-structured CSS for AI text area integration

The CSS changes effectively handle the AI-specific styling requirements while maintaining consistency with the existing design.

packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (1)

369-369: Verify prop name consistency.

The is-edit-column prop passed to AiSettings component needs verification for consistency with the component's prop definition.

packages/nc-gui/composables/useIntegrationsStore.ts (4)

93-94: LGTM: Clean integration of AI state management

The addition of aiIntegrations from useNocoAi composable follows Vue's composition pattern well.


191-192: LGTM: Consistent event tracking implementation

Event tracking is well-implemented with:

  • Consistent placement before operations
  • Clear event naming
  • Proper differentiation between create and duplicate operations

Also applies to: 224-225, 253-254, 287-291


541-541: LGTM: Clean export of new functionality

The loadDynamicIntegrations function is properly exported as part of the store's public API.


229-238: ⚠️ Potential issue

Avoid mutating objects within map functions

The code still contains object mutations within map functions which can lead to reactivity issues in Vue.

Apply these fixes:

// First instance
aiIntegrations.value = aiIntegrations.value.map((i) => 
  i.id === integration.id 
    ? { ...i, title: integration.title }
    : i
)

// Second instance
aiIntegrations.value = aiIntegrations.value.map((i) => 
  i.id === integration.id
    ? { ...i, is_default: true }
    : { ...i, is_default: false }
)

Also applies to: 258-269

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (3)

Line range hint 1-7: LGTM: Import changes are clean and follow conventions

The addition of isAIPromptCol to the imports is well-organized and follows the existing pattern.


1281-1296: 🛠️ Refactor suggestion

Add validation and error handling for AI prompt column import

The AI prompt column import lacks validation and error handling, as noted in a previous review.

Apply this diff to add validation and error handling:

 } else if (isAIPromptCol(col)) {
   if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
     colOptions.fk_workspace_id = null;
     colOptions.fk_integration_id = null;
     colOptions.model = null;
   }

+  // Validate required fields
+  if (!colOptions.prompt_raw) {
+    this.logger.warn(`Skipping AI prompt column import: Missing prompt_raw for column ${col.title}`);
+    continue;
+  }

+  try {
     const freshModelData = await this.columnsService.columnAdd(context, {
       tableId: getIdOrExternalId(getParentIdentifier(col.id)),
       column: withoutId({
         ...flatCol,
         ...{
           fk_integration_id: colOptions.fk_integration_id,
           model: colOptions.model,
           prompt_raw: colOptions.prompt_raw,
         },
       }) as any,
       req: param.req,
       user: param.user,
     });
+  } catch (error) {
+    this.logger.error(`Failed to import AI prompt column ${col.title}: ${error.message}`);
+    throw error;
+  }

180-183: Verify the isAIPromptCol function implementation

The addition of !isAIPromptCol(a) to filter out AI prompt columns is logically sound. However, we should verify the implementation of this function.

✅ Verification successful

isAIPromptCol function is properly implemented and safe to use

The function is well-defined in packages/nocodb-sdk/src/lib/UITypes.ts with proper dependencies:

  • Uses UITypes.LongText enum value which is correctly defined
  • parseHelper is a robust utility that safely handles JSON parsing
  • LongTextAiMetaProp is properly defined as a constant with value 'ai'
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of isAIPromptCol function
# Expected: Find the implementation to ensure it's properly defined

# Search for isAIPromptCol implementation
ast-grep --pattern 'function isAIPromptCol($_) {
  $$$
}'

# Search for isAIPromptCol type definition
rg -A 5 'export (type|interface).*isAIPromptCol'

Length of output: 589


Script:

#!/bin/bash
# Let's verify the dependencies of isAIPromptCol function

# Check for parseHelper and LongTextAiMetaProp
rg "parseHelper|LongTextAiMetaProp" packages/nocodb-sdk/src/lib/UITypes.ts -A 5

# Check UITypes.LongText definition
rg "UITypes.LongText|LongText =" packages/nocodb-sdk/src/lib/UITypes.ts -A 2

Length of output: 938


Script:

#!/bin/bash
# Let's verify the parseHelper implementation and LongTextAiMetaProp definition

# Check parseHelper implementation
rg -p "export.*parseHelper" packages/nocodb-sdk/src/lib/helperFunctions.ts -A 5

# Check LongTextAiMetaProp definition
rg "export.*LongTextAiMetaProp" -A 2

Length of output: 700

packages/nocodb/src/models/AIColumn.ts (3)

14-16: Ensure correct instantiation in the castType method

The castType method should handle cases where data might be null or undefined. Currently, if data is falsy, the method returns data, which could be undefined instead of an AIColumn instance. This might lead to unexpected behavior downstream.

Consider explicitly handling falsy values:

-     return data && new AIColumn(data);
+     return data ? new AIColumn(data) : null;

26-26: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-       return this._insert(
+       return AIColumn._insert(
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-       return this._update(
+       return AIColumn._update(
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-       return this.read(context, longTextColumn.fk_column_id, ncMeta);
+       return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

-       return column ? this.castType(column) : null;
+       return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nc-gui/components/cell/AI.vue (2)

81-92: Consider adding error handling for AI response processing

The code processes AI responses without explicit error handling, which could lead to runtime errors if the response format is unexpected.

Consider adding try-catch blocks to handle potential errors and provide user feedback.


131-135: Fix the tooltip disabled logic

The tooltip's disabled condition seems incorrect. It should be disabled when the button is enabled, not the other way around.

Consider changing:

-        <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+        <NcTooltip :disabled="!isDisabledAiButton" class="flex">

This ensures the tooltip is only shown when the button is disabled.

packages/nocodb/src/db/sortV2.ts (1)

203-221: Remove duplicate condition check

The isAIPromptCol(column) condition is checked twice unnecessarily. Removing the duplicate check simplifies the code.

Apply this fix:

case UITypes.LongText: {
  if (isAIPromptCol(column)) {
    let col;
-   if (isAIPromptCol(column)) {
    if (knex.clientType() === 'pg') {
      col = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [
        column.column_name,
      ]);
    } else if (knex.clientType().startsWith('mysql')) {
      col = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [
        column.column_name,
      ]);
    } else if (knex.clientType() === 'sqlite3') {
      col = knex.raw(`json_extract(??, '$.value')`, [
        column.column_name,
      ]);
    } else if (knex.clientType() === 'mssql') {
      col = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
    }
-   }
    qb.orderBy(col, sort.direction || 'asc', nulls);
  } else {
    qb.orderBy(
      sanitize(column.column_name),
      sort.direction || 'asc',
      nulls,
    );
  }
  break;
}
packages/nc-gui/components/ai/PromptWithFields.vue (1)

159-174: Add tooltip cleanup on component unmount

The tooltips are properly initialized but should be cleaned up when the component is unmounted to prevent memory leaks.

Consider adding cleanup code in onBeforeUnmount:

+ const tooltipInstances: any[] = []

  function loadMentionFieldTagTooltip() {
    document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
      const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
      if (!tooltip || el.scrollWidth <= el.clientWidth) return
      // Show tooltip only on truncate
-     tippy(el, {
+     const instance = tippy(el, {
          content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
          placement: 'top',
          allowHTML: true,
          arrow: true,
          animation: 'fade',
          duration: 0,
          maxWidth: '200px',
        })
+     tooltipInstances.push(instance)
    })
  }

+ onBeforeUnmount(() => {
+   tooltipInstances.forEach(instance => instance.destroy())
+   tooltipInstances.length = 0
+ })

This change ensures that tooltip instances are properly destroyed when the component is unmounted, preventing potential memory leaks.

packages/nc-gui/components/ai/Settings.vue (3)

10-15: LGTM: Props definition is clear and well-structured

The addition of the isEditColumn prop with a default value of false is clean and follows Vue best practices.


215-221: LGTM: Scoped styling follows best practices

The added styles use proper scoping and SCSS nesting with appropriate deep selector usage.


70-73: Verify the impact of early return

The early return when isEditColumn is true prevents initialization of AI models. Ensure this doesn't affect existing functionality where model data might be needed even in edit mode.

✅ Verification successful

Early return in edit mode is correct and safe

The early return when isEditColumn is true is safe because:

  1. The component is a settings panel that allows selecting AI integration and model
  2. The early return only skips the initial model/integration setup in onMounted hook
  3. The component still renders all UI elements and maintains reactive bindings
  4. The skip only prevents auto-selecting default values, which is appropriate for edit mode
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of this component with isEditColumn prop
rg -A 5 '<Settings.*:is-edit-column'

Length of output: 38


Script:

#!/bin/bash
# Let's first find where Settings component is used
rg -l "Settings" --type vue

# Then let's examine the component itself to understand the context
rg -A 10 "isEditColumn" packages/nc-gui/components/ai/Settings.vue

Length of output: 965


Script:

#!/bin/bash
# Let's search for the file with .vue extension explicitly
rg -l "Settings\.vue" 

# And let's get more context about the component's usage of models
rg -B 5 -A 10 "const vModel = useVModel" packages/nc-gui/components/ai/Settings.vue

Length of output: 544


Script:

#!/bin/bash
# Let's look at the full component implementation to understand the flow
cat packages/nc-gui/components/ai/Settings.vue

# Also search for any references to this component in the codebase
rg -l "ai/Settings"

Length of output: 7736

packages/nc-gui/composables/useNocoAi.ts (2)

20-21: LGTM: Type enhancement improves flexibility

The change to Partial<IntegrationType>[] makes the type more flexible while maintaining type safety.


24-28: ⚠️ Potential issue

Fix computed property access in isAiIntegrationAvailableInList

The function has a bug where it's using the computed property name instead of its value.

-    if (!aiIntegrationAvailable) return false
+    if (!aiIntegrationAvailable.value) return false

Likely invalid or redundant comment.

packages/nc-gui/utils/columnUtils.ts (4)

15-17: LGTM: Constants are well-defined

The addition of AIPrompt constant and its export maintains consistency with existing patterns.


27-32: LGTM: UI type definition follows existing pattern

The new AIPrompt UI type is well-structured and consistent with other type definitions.


314-316: 🛠️ Refactor suggestion

Remove redundant Formula type check

The check for UITypes.Formula is performed twice: once within the switch statement and once afterward.

-  if (col.uidt === UITypes.Formula) {
-    result.isInvalid = !!(col.colOptions as FormulaType).error
-  }

Likely invalid or redundant comment.


289-309: 🛠️ Refactor suggestion

DRY up duplicate AI validation logic

The AI integration validation logic is duplicated between Button and LongText types.

Consider extracting the common logic into a helper function:

+const isAiColumnInvalid = (colOptions: ButtonType, aiIntegrations: Partial<IntegrationType>[], isReadOnly: boolean) => ({
+  isInvalid:
+    !colOptions.fk_integration_id ||
+    (isReadOnly
+      ? false
+      : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')),
+  tooltip: 'title.aiIntegrationMissing'
+})

 case UITypes.Button: {
   const colOptions = col.colOptions as ButtonType
   if (colOptions.type === ButtonActionsType.Webhook) {
     result.isInvalid = !colOptions.fk_webhook_id
   } else if (colOptions.type === ButtonActionsType.Url) {
     result.isInvalid = !!colOptions.error
   } else if (colOptions.type === ButtonActionsType.Ai) {
-    result.isInvalid =
-      !colOptions.fk_integration_id ||
-      (isReadOnly
-        ? false
-        : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-    result.tooltip = 'title.aiIntegrationMissing'
+    const validation = isAiColumnInvalid(colOptions, aiIntegrations, isReadOnly)
+    result.isInvalid = validation.isInvalid
+    result.tooltip = validation.tooltip
   }
   break
 }
 case UITypes.LongText: {
   if (parseProp(col.meta)[LongTextAiMetaProp]) {
     const colOptions = col.colOptions as ButtonType
-    result.isInvalid =
-      !colOptions.fk_integration_id ||
-      (isReadOnly
-        ? false
-        : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-    result.tooltip = 'title.aiIntegrationMissing'
+    const validation = isAiColumnInvalid(colOptions, aiIntegrations, isReadOnly)
+    result.isInvalid = validation.isInvalid
+    result.tooltip = validation.tooltip
   }
 }

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/details/Fields.vue (2)

Line range hint 2255-2261: LGTM! Improved expand icon styling

The changes enhance the visual feedback and hover states while maintaining consistency with the design system.


1040-1043: ⚠️ Potential issue

Replace undefined ncDelay with delay from @vueuse/core

The ncDelay function is not defined or imported, which will lead to a runtime error.

Apply this diff to fix the issue:

+import { delay } from '@vueuse/core'

 await copy(field.id!)
 isFieldIdCopied.value = true
-await ncDelay(5000)
+await delay(5000)
 isFieldIdCopied.value = false
packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (2)

1025-1026: LGTM! Improved AI column type checking

The changes correctly identify AI-related columns by checking both AI prompt columns and AI buttons.


1049-1049: LGTM! Robust output column handling for AI buttons

The code properly handles output column IDs from button options, including graceful handling of empty or malformed values.

packages/nc-gui/components/smartsheet/grid/Table.vue (2)

855-856: LGTM! Consistent AI column type checking

The changes maintain consistency with InfiniteTable.vue in handling AI prompt columns and AI buttons.


2140-2146: LGTM! Consistent expand icon styling

The changes maintain visual consistency with Fields.vue in styling and behavior of expand icons.

packages/nocodb/src/services/columns.service.ts (4)

1578-1600: ⚠️ Potential issue

Add transaction management and error handling for data conversion

The conversion from AI to non-AI format should be wrapped in a transaction to maintain data consistency. Additionally, error handling should be added to handle conversion failures gracefully.

Apply this diff to add transaction management:

      if (
        isAIPromptCol(column) &&
        (colBody.uidt !== UITypes.LongText ||
          (colBody.uidt === UITypes.LongText &&
            colBody.meta?.[LongTextAiMetaProp] !== true))
      ) {
        const baseModel = await reuseOrSave('baseModel', reuse, async () =>
          Model.getBaseModelSQL(context, {
            id: table.id,
            dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
              NcConnectionMgrv2.get(source),
            ),
          }),
        );

+       // Start transaction
+       const trx = await sqlClient.knex.transaction();
+       try {
          await convertAIRecordTypeToValue({
            source,
            table,
            column,
            baseModel,
            sqlClient,
          });
+         await trx.commit();
+       } catch (error) {
+         await trx.rollback();
+         NcError.badRequest(`Failed to convert AI column: ${error.message}`);
+       }
      }

1601-1621: ⚠️ Potential issue

Add security checks for prompt template processing

The prompt template processing needs additional security checks:

  1. Verify user permissions for referenced columns
  2. Sanitize input to prevent SQL injection
  3. Validate template complexity to prevent DoS attacks

Apply this diff to add security checks:

        if (colBody.prompt_raw) {
          await table.getColumns(context);

+         // Validate template complexity
+         if (this.getTemplateComplexity(colBody.prompt_raw) > MAX_TEMPLATE_COMPLEXITY) {
+           NcError.badRequest('Prompt template too complex');
+         }

          prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
            const column = table.columns.find((c) => c.title === p1);

            if (!column) {
              NcError.badRequest(`Field '${p1}' not found`);
            }

+           // Verify user has permission to access the column
+           if (!this.hasColumnPermission(context, column, param.user)) {
+             NcError.forbidden(`No access to field '${p1}'`);
+           }
+
+           // Sanitize column reference
+           if (!this.isValidColumnReference(p1)) {
+             NcError.badRequest(`Invalid field reference '${p1}'`);
+           }

            return `{${column.id}}`;
          });
        }

1622-1645: ⚠️ Potential issue

Add validation and error handling for data conversion

The data conversion from non-AI to AI format lacks proper validation and error handling. Consider adding checks for data integrity and handling conversion failures gracefully.

Apply this diff to add validation and error handling:

        if (
          column.uidt !== UITypes.LongText ||
          column.meta?.[LongTextAiMetaProp] !== true
        ) {
          const baseModel = await reuseOrSave('baseModel', reuse, async () =>
            Model.getBaseModelSQL(context, {
              id: table.id,
              dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
                NcConnectionMgrv2.get(source),
              ),
            }),
          );

+         // Start transaction
+         const trx = await sqlClient.knex.transaction();
+         try {
+           // Validate data before conversion
+           const validationResult = await baseModel.validateData({
+             column,
+             user: param.user
+           });
+           
+           if (!validationResult.valid) {
+             throw new Error(`Data validation failed: ${validationResult.message}`);
+           }
+
            await convertValueToAIRecordType({
              source,
              table,
              column,
              baseModel,
              sqlClient,
              user: param.user,
            });
+           
+           await trx.commit();
+         } catch (error) {
+           await trx.rollback();
+           NcError.badRequest(`Failed to convert to AI format: ${error.message}`);
+         }
        }

2282-2304: 🛠️ Refactor suggestion

Refactor duplicate prompt processing logic and add validations

The prompt processing logic is duplicated between column update and add operations. Additionally, there are missing validations for AI integration requirements.

Apply this diff to refactor and add validations:

+     private async processPromptTemplate(
+       context: NcContext,
+       table: Model,
+       promptRaw: string
+     ): Promise<string> {
+       await table.getColumns(context);
+       
+       // Validate prompt template format
+       const MAX_PROMPT_LENGTH = 1000;
+       if (promptRaw.length > MAX_PROMPT_LENGTH) {
+         NcError.badRequest(`Prompt template exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`);
+       }
+       
+       // Check for valid template syntax
+       const templatePattern = /^[^{}]*(\{[^{}]+\}[^{}]*)*$/;
+       if (!templatePattern.test(promptRaw)) {
+         NcError.badRequest('Invalid prompt template format');
+       }
+
+       return promptRaw.replace(/{(.*?)}/g, (match, p1) => {
+         const column = table.columns.find((c) => c.title === p1);
+         if (!column) {
+           NcError.badRequest(`Field '${p1}' not found`);
+         }
+         return `{${column.id}}`;
+       });
+     }

      if (isAIPromptCol(colBody)) {
+       // Validate AI integration requirements
+       if (!colBody.meta?.aiEnabled) {
+         NcError.badRequest('AI integration must be enabled for AI prompt columns');
+       }
+
+       // Validate AI model configuration
+       if (!colBody.meta?.aiModel) {
+         NcError.badRequest('AI model must be specified for AI prompt columns');
+       }

        let prompt = '';

        if (colBody.prompt_raw) {
-         await table.getColumns(context);
-         prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
-           const column = table.columns.find((c) => c.title === p1);
-           if (!column) {
-             NcError.badRequest(`Field '${p1}' not found`);
-           }
-           return `{${column.id}}`;
-         });
+         prompt = await this.processPromptTemplate(context, table, colBody.prompt_raw);
        }

        colBody.prompt = prompt;
      }
packages/nocodb/src/db/BaseModelSqlv2.ts (4)

10062-10137: Implement AI prompt column handling

New AI prompt column handling has been added with proper state tracking and metadata management.

The implementation:

  1. Handles stale state tracking for AI columns
  2. Manages last modified metadata (user and timestamp)
  3. Properly differentiates between system and user modifications
  4. Tracks referenced column changes to mark AI columns as stale
  5. Uses proper JSON serialization for storage

The code follows best practices and includes proper error handling.


9778-9778: Add raw flag parameter to prepareNocoData()

Added extra parameter with raw flag to control AI prompt handling behavior.

The raw flag parameter allows bypassing AI prompt handling when needed, improving flexibility of the method.


5721-5723: ⚠️ Potential issue

Fix Promise handling in bulkInsert()

The Promise handling has been improved by properly awaiting all promises using Promise.all().

Apply this diff to fix the unhandled Promise rejections:

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.


8635-8653: 🛠️ Refactor suggestion

Improve JSON parsing error handling

The JSON parsing error handling has been improved with proper validation and error handling.

Apply this diff to improve error handling:

if (d) {
  for (const col of jsonColumns) {
    if (d[col.id] && typeof d[col.id] === 'string') {
      try {
        d[col.id] = JSON.parse(d[col.id]);
+       // Validate expected data structure
+       if (typeof d[col.id] !== 'object') {
+         throw new Error('Invalid JSON structure');
+       }
-     } catch {}
+     } catch (e) {
+       logger.warn(`Error parsing JSON for column ${col.id}: ${e.message}`);
+       d[col.id] = null; // Set to null on parse error
+     }
    }

    if (d[col.id]?.length) {
      for (let i = 0; i < d[col.id].length; i++) {
        if (typeof d[col.id][i] === 'string') {
          try {
            d[col.id][i] = JSON.parse(d[col.id][i]);
+           // Validate array element structure
+           if (typeof d[col.id][i] !== 'object') {
+             throw new Error('Invalid JSON array element');
+           }
-         } catch {}
+         } catch (e) {
+           logger.warn(`Error parsing JSON array element: ${e.message}`);
+           d[col.id][i] = null;
+         }
        }
      }
    }
  }
}

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (1)

Line range hint 249-267: LGTM! AI prompt type handling looks good

The type selection logic correctly sets up the AI prompt column with proper metadata and UI type.

packages/nocodb/src/models/Column.ts (3)

573-577: LGTM! AI column reading implementation looks good

The implementation correctly handles reading AI column options.


886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect. It should check for the actual column ID.

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


452-469: 🛠️ Refactor suggestion

Add error handling for AI column insertion

The AI column insertion lacks error handling which could lead to silent failures.

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   try {
      await AIColumn.insert(
        context,
        {
          fk_model_id: column.fk_model_id,
          fk_column_id: colId,
          fk_integration_id: column.fk_integration_id,
          model: column.model,
          prompt: column.prompt,
          prompt_raw: column.prompt_raw,
          error: column.error,
        },
        ncMeta,
      );
+   } catch (error) {
+     logger.error(`Failed to insert AI column: ${error.message}`);
+     throw error;
+   }
  }
  break;
}

Likely invalid or redundant comment.

Comment on lines 596 to 613
if (isAIPromptCol(column)) {
if (knex.clientType() === 'pg') {
field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [
column.column_name,
]);
} else if (knex.clientType().startsWith('mysql')) {
field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [
column.column_name,
]);
} else if (knex.clientType() === 'sqlite3') {
field = knex.raw(`json_extract(??, '$.value')`, [
column.column_name,
]);
} else if (knex.clientType() === 'mssql') {
field = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for JSON extraction

The JSON extraction logic should handle invalid JSON values to prevent runtime errors.

if (isAIPromptCol(column)) {
+ const handleInvalidJson = (field) => {
+   return knex.raw('CASE WHEN JSON_VALID(??) THEN ? ELSE NULL END', [
+     column.column_name,
+     field
+   ]);
+ };

  if (knex.clientType() === 'pg') {
-   field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name])
+   );
  } else if (knex.clientType().startsWith('mysql')) {
-   field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name])
+   );
  } // Apply similar changes for other DB types
}

Committable suggestion skipped: line range outside the PR's diff.

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch from 43ddfc6 to c2d7348 Compare December 7, 2024 09:49
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (14)
packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2)

Line range hint 174-212: Use i18n for validation messages

The validation message for formula_raw is hardcoded in English. Consider using the i18n system for consistency with other messages.

 formula_raw: [
   {
     required: true,
-    message: 'Prompt required for AI Button',
+    message: t('msg.promptRequiredForAiButton'),
   },
 ],

Line range hint 212-224: Consider using constants for default values

Move the hardcoded default values for AI button properties to constants for better maintainability and reusability.

+const AI_BUTTON_DEFAULTS = {
+  theme: 'light',
+  label: 'Generate data',
+  color: 'purple',
+  icon: 'ncAutoAwesome',
+} as const;

 if (vModel.value.type === ButtonActionsType.Ai) {
-  vModel.value.theme = 'light'
-  vModel.value.label = 'Generate data'
-  vModel.value.color = 'purple'
-  vModel.value.icon = 'ncAutoAwesome'
+  Object.assign(vModel.value, AI_BUTTON_DEFAULTS)
   vModel.value.output_column_ids = ''
 }
packages/nc-gui/composables/useIntegrationsStore.ts (2)

Line range hint 447-496: Consider using immutable state updates

The function mutates the global allIntegrations array directly with push. Consider using immutable updates for better state management.

- allIntegrations.push(integration)
+ allIntegrations = [...allIntegrations, integration]

Additionally, consider adding type safety improvements:

interface IntegrationMeta {
  title?: string
  icon?: string
  description?: string
  order?: number
}

interface DynamicIntegration {
  type: IntegrationsType
  subType: string
  meta: IntegrationMeta
}

296-296: Use optional chaining for safer null checks

Consider using optional chaining for better null safety.

- if (response && response?.id)
+ if (response?.id)
🧰 Tools
🪛 Biome (1.9.4)

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nc-gui/utils/columnUtils.ts (1)

269-273: Consider adding type guard for aiIntegrations parameter

The aiIntegrations parameter is typed as Partial<IntegrationType>[] but the code assumes certain properties exist. Consider adding a type guard to ensure type safety.

-const isColumnInvalid = (
+const isColumnInvalid = (
   col: ColumnType,
-  aiIntegrations: Partial<IntegrationType>[] = [],
+  aiIntegrations: Pick<IntegrationType, 'id'>[] = [],
   isReadOnly: boolean = false,
 ): { isInvalid: boolean; tooltip: string } => {
packages/nc-gui/components/ai/Settings.vue (2)

10-11: Add prop validation for isEditColumn.

Consider adding runtime validation for the isEditColumn prop to ensure type safety.

const props = withDefaults(
  defineProps<{
    fkIntegrationId?: string
    model?: string
    randomness?: string
    workspaceId: string
    scope?: string
    showTooltip?: boolean
-   isEditColumn?: boolean
+   isEditColumn?: boolean & { type: Boolean }
  }>(),
  {
    showTooltip: true,
    isEditColumn: false,
  },
)

Line range hint 82-104: Add error handling for the generateRows asynchronous call.

The generate function lacks error handling for the generateRows call.

- const res = await generateRows(
-   meta.value.id!,
-   {
-     title: previewFieldTitle.value,
-     prompt_raw: vModel.value.prompt_raw,
-     fk_integration_id: vModel.value.fk_integration_id,
-     uidt: UITypes.LongText,
-   },
-   [pk],
- )
+ try {
+   const res = await generateRows(
+     meta.value.id!,
+     {
+       title: previewFieldTitle.value,
+       prompt_raw: vModel.value.prompt_raw,
+       fk_integration_id: vModel.value.fk_integration_id,
+       uidt: UITypes.LongText,
+     },
+     [pk],
+   )
+   if (res?.length && res[0]?.[previewFieldTitle.value]) {
+     previewRow.value.row = {
+       ...res[0],
+       [previewFieldTitle.value]: {
+         value: res[0]?.[previewFieldTitle.value],
+       },
+     }
+     isAlreadyGenerated.value = true
+   }
+ } catch (error) {
+   message.error(`Failed to generate preview: ${error.message}`)
+ } finally {
+   generatingPreview.value = false
+ }
packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

126-140: Simplify conditional logic in handleDisableSubmitBtn.

The function can be simplified to improve readability.

- const handleDisableSubmitBtn = () => {
-   if (!isEnabledGenerateText.value) {
-     if (disableSubmitBtn.value) {
-       disableSubmitBtn.value = false
-     }
-     return
-   }
-   if (isPreviewEnabled.value) {
-     disableSubmitBtn.value = false
-   } else {
-     disableSubmitBtn.value = true
-   }
- }
+ const handleDisableSubmitBtn = () => {
+   disableSubmitBtn.value = isEnabledGenerateText.value && !isPreviewEnabled.value
+ }

150-166: Consider extracting validation logic.

The watch effect contains validation logic that could be moved to a separate function for better maintainability.

+ const updateValidations = (hasAiEnabled: boolean) => {
+   setAdditionalValidations({
+     fk_integration_id: [{ 
+       required: hasAiEnabled, 
+       message: hasAiEnabled ? t('title.aiIntegrationMissing') : undefined 
+     }],
+   })
+ }
+
watch(
  isEnabledGenerateText,
- (newValue) => {
-   if (newValue) {
-     setAdditionalValidations({
-       fk_integration_id: [{ required: true, message: t('title.aiIntegrationMissing') }],
-     })
-   } else {
-     setAdditionalValidations({
-       fk_integration_id: [{ required: false }],
-     })
-   }
- },
+ updateValidations,
  { immediate: true }
)
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (1)

1098-1104: Consider using a more maintainable approach for icon selection

The nested ternary operations make the code harder to read and maintain.

Consider refactoring to a more readable approach:

-:is="
-  isAiButtonSelectOption(opt.name) && !isColumnTypeOpen
-    ? iconMap.cellAiButton
-    : isAiPromptSelectOption(opt.name) && !isColumnTypeOpen
-    ? iconMap.cellAi
-    : opt.icon
-"
+:is="getColumnTypeIcon(opt.name, isColumnTypeOpen)"

Add a helper method:

function getColumnTypeIcon(name: string, isOpen: boolean) {
  if (!isOpen) {
    if (isAiButtonSelectOption(name)) return iconMap.cellAiButton;
    if (isAiPromptSelectOption(name)) return iconMap.cellAi;
  }
  return opt.icon;
}
packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (1)

1051-1051: Improve error handling in AI bulk generation

The AI bulk generation logic could benefit from more robust error handling and user feedback.

if (isAiButton(field)) {
+ try {
    outputColumnIds =
      ncIsString(field.colOptions?.output_column_ids) && field.colOptions.output_column_ids.split(',').length > 0
        ? field.colOptions.output_column_ids.split(',')
        : []
+ } catch (error) {
+   console.error('Failed to parse output column IDs:', error)
+   message.error('Failed to process AI button configuration')
+   return
+ }
}
packages/nc-gui/components/smartsheet/grid/Table.vue (2)

Line range hint 879-916: Add validation and error handling for AI bulk generation

The function now properly handles:

  • Output column IDs for AI button columns
  • Validation of column existence
  • Proper cleanup of generating states

Consider adding error handling for failed row updates:

  if (res) {
+   try {
      for (const row of res) {
        const oldRow = dataRef.value.find(
          (r) => extractPkFromRow(r.row, meta.value!.columns!) === extractPkFromRow(row, meta.value!.columns!),
        );

        if (oldRow) {
          oldRow.row = { ...oldRow.row, ...row }
        }
      }
+   } catch (error) {
+     console.error('Failed to update rows:', error)
+   }
  }

2140-2146: Improve expand button styling and accessibility

The expand button styling has been enhanced with proper border and hover states.

Consider adding ARIA labels for better accessibility:

  <component
    :is="iconMap.maximize"
    v-if="expandForm"
    v-e="['c:row-expand:open']"
    class="select-none nc-row-expand opacity-90 w-4 h-4"
+   aria-label="Expand row details"
    @click="expandAndLooseFocus(row, state)"
  />
packages/nocodb/src/services/columns.service.ts (1)

1622-1645: Add data conversion for non-AI to AI format

The code handles converting data to AI format when changing column type to AI.

Add transaction handling to ensure data consistency:

+ const trx = await sqlClient.knex.transaction();
+ try {
    await convertValueToAIRecordType({
      source,
      table,
      column,
      baseModel,
      sqlClient,
      user: param.user,
    });
+   await trx.commit();
+ } catch (error) {
+   await trx.rollback();
+   throw new Error(`Failed to convert to AI format: ${error.message}`);
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 43ddfc6 and c2d7348.

📒 Files selected for processing (69)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (11 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (43)
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/models/index.ts
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/nc/Button.vue
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/components/general/Loader.vue
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nocodb/src/db/sortV2.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nocodb/src/utils/globals.ts
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/smartsheet/Kanban.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (46)
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: 🛠️ Refactor suggestion

The ncDelay function is undefined

The function ncDelay is called but not imported or defined within this component.

Additionally, consider these improvements:

  1. The 5-second delay might be too long for user feedback. A 2-3 second delay is typically sufficient.
  2. Add error handling for the copy operation.
 const  () => {
+  try {
     await copy(view.value!.id!)
     isViewIdCopied.value = true
 
-    await ncDelay(5000)
+    await new Promise(resolve => setTimeout(resolve, 2000))
 
     isViewIdCopied.value = false
+  } catch (e: any) {
+    message.error(t('msg.error.copyToClipboardError'))
+  }
 }
packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1)

Line range hint 1-724: Overall implementation looks good!

The changes effectively implement AI button configuration with proper validation and initialization. The code is well-structured and follows Vue.js best practices.

packages/nc-gui/composables/useIntegrationsStore.ts (4)

93-94: LGTM: AI Integration State Management

The integration with the useNocoAi composable is properly implemented.


191-192: LGTM: Event Tracking Implementation

Event tracking is consistently implemented across all integration operations with proper naming conventions.

Also applies to: 224-225, 253-254, 287-292


541-541: LGTM: Store Export

The loadDynamicIntegrations function is properly exported in the store's return object.


229-238: ⚠️ Potential issue

Fix object mutations in map functions

The code mutates objects within map functions, which can lead to reactivity issues in Vue.

Previous review comments have already addressed this issue. Please implement the suggested fixes for both instances:

  1. For title update (lines 229-238):
if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => 
    i.id === integration.id 
      ? { ...i, title: integration.title }
      : i
  )
}
  1. For default status update (lines 258-269):
if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => 
    i.id === integration.id
      ? { ...i, is_default: true }
      : { ...i, is_default: false }
  )
}

Also applies to: 258-269

packages/nc-gui/utils/columnUtils.ts (4)

15-18: LGTM: Constants are well-defined and properly exported

The new constants AIPrompt and LongTextAiMetaProp are clearly defined and follow the existing pattern.


27-32: LGTM: UI type definition follows established pattern

The new AIPrompt UI type is well-structured with appropriate flags for feature management.


289-296: 🛠️ Refactor suggestion

Extract duplicate AI validation logic

The AI integration validation logic is duplicated between Button and LongText types.

Extract the common logic into a helper function:

+const isAiColumnInvalid = (
+  colOptions: ButtonType,
+  aiIntegrations: Pick<IntegrationType, 'id'>[],
+  isReadOnly: boolean
+): { isInvalid: boolean; tooltip: string } => ({
+  isInvalid:
+    !colOptions.fk_integration_id ||
+    (isReadOnly
+      ? false
+      : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')),
+  tooltip: 'title.aiIntegrationMissing'
+})

 const isColumnInvalid = (
   col: ColumnType,
-  aiIntegrations: Partial<IntegrationType>[] = [],
+  aiIntegrations: Pick<IntegrationType, 'id'>[] = [],
   isReadOnly: boolean = false,
 ): { isInvalid: boolean; tooltip: string } => {
   // ...
   case ButtonActionsType.Ai: {
-    result.isInvalid =
-      !colOptions.fk_integration_id ||
-      (isReadOnly
-        ? false
-        : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-    result.tooltip = 'title.aiIntegrationMissing'
+    const aiValidation = isAiColumnInvalid(colOptions, aiIntegrations, isReadOnly)
+    result.isInvalid = aiValidation.isInvalid
+    result.tooltip = aiValidation.tooltip
   }
   // ...
   case UITypes.LongText: {
     if (parseProp(col.meta)[LongTextAiMetaProp]) {
       const colOptions = col.colOptions as ButtonType
-      result.isInvalid =
-        !colOptions.fk_integration_id ||
-        (isReadOnly
-          ? false
-          : !!colOptions.fk_integration_id && !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-      result.tooltip = 'title.aiIntegrationMissing'
+      const aiValidation = isAiColumnInvalid(colOptions, aiIntegrations, isReadOnly)
+      result.isInvalid = aiValidation.isInvalid
+      result.tooltip = aiValidation.tooltip
     }
   }

Also applies to: 299-309


314-317: 🛠️ Refactor suggestion

Remove redundant Formula type check

The check for UITypes.Formula is performed twice: once within the switch statement and once afterward.

Remove the redundant check:

-  if (col.uidt === UITypes.Formula) {
-    result.isInvalid = !!(col.colOptions as FormulaType).error
-  }
packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (2)

369-369: LGTM: Prop addition follows Vue.js conventions

The addition of :is-edit-column prop with the isEdit value from the store follows Vue.js naming conventions and best practices.


471-475: LGTM: Tooltip implementation improves user experience

The tooltip is properly implemented with the show-on-truncate-only prop, ensuring it appears only when the text is truncated. This follows best practices for progressive enhancement of the UI.

packages/nocodb/src/models/AIColumn.ts (2)

14-16: ⚠️ Potential issue

Ensure consistent return types in castType method

The castType method may return undefined if data is falsy, leading to inconsistent return types. It's better to explicitly return null when data is falsy to maintain consistency.

Apply this diff to fix the issue:

-    return data && new AIColumn(data);
+    return data ? new AIColumn(data) : null;

26-26: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be confusing because this refers to the class, not an instance. It's clearer to reference the class name directly.

Apply this diff to fix the issue:

At line 26:

-        return this._insert(
+        return AIColumn._insert(

At line 40:

-        return this._update(
+        return AIColumn._update(

Also applies to: 40-40

🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (1)

61-61: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can lead to confusion since this refers to the class itself in a static context. It's clearer to use the class name directly.

Apply this diff to fix the issue:

At line 61:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);

At line 85:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;

Also applies to: 85-85

🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

135-149: ⚠️ Potential issue

Avoid string concatenation in SQL queries to prevent SQL injection

In the MSSQL query, string concatenation is used to build the JSON string, which can lead to SQL injection vulnerabilities. It's important to use parameterized queries or proper JSON construction methods to prevent security risks.

Consider refactoring the query to use parameter bindings or MSSQL's JSON functions without concatenation. For example, you can use OPENJSON or FOR JSON to construct JSON objects safely.

Apply this diff to fix the issue:

-        await sqlClient.raw(
-          `UPDATE ??
-            SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}')
-            WHERE ?? is not null;`,
-          [
-            baseModel.getTnPath(table.table_name),
-            column.column_name,
-            column.column_name,
-            commonRecord.lastModifiedBy.toString(),
-            commonRecord.lastModifiedTime.toString(),
-            commonRecord.isStale,
-            column.column_name,
-          ],
-        );
+        await sqlClient.raw(
+          `UPDATE ??
+            SET ?? = JSON_QUERY(?)
+            WHERE ?? IS NOT NULL;`,
+          [
+            baseModel.getTnPath(table.table_name),
+            column.column_name,
+            JSON.stringify({
+              value: sqlClient.ref(column.column_name),
+              lastModifiedBy: commonRecord.lastModifiedBy.toString(),
+              lastModifiedTime: commonRecord.lastModifiedTime.toString(),
+              isStale: commonRecord.isStale,
+            }),
+            column.column_name,
+          ],
+        );

Likely invalid or redundant comment.

packages/nc-gui/components/cell/AI.vue (2)

81-92: Consider adding error handling for AI response processing

The code processes AI responses without explicit error handling, which could lead to runtime errors if the response format is unexpected.

Consider wrapping the response processing in try...catch blocks to handle any potenti F438 al errors gracefully.

Apply this diff to enhance error handling:

if (obj && typeof obj === 'object') {
+  try {
    vModel.value = obj
    setTimeout(() => {
      isAiEdited.value = false
    }, 100)
+  } catch (error) {
+    console.error('Error processing AI response:', error)
+    // Optionally, display a user-friendly error message
+  }
} else {
+  try {
    vModel.value = {
      ...(ncIsObject(vModel.value) ? vModel.value : {}),
      isStale: false,
      value: resRow[column.value.title!],
    }
+  } catch (error) {
+    console.error('Error setting AI response value:', error)
+    // Optionally, display a user-friendly error message
+  }
}

131-135: ⚠️ Potential issue

Fix the tooltip disabled logic

The current logic for disabling the tooltip appears to be inverted. The tooltip should be disabled when the button is enabled, not when it is disabled.

Consider updating the :disabled prop of NcTooltip to reflect the correct logic.

Apply this diff to fix the issue:

-        <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+        <NcTooltip :disabled="!isDisabledAiButton" class="flex">
packages/nc-gui/components/ai/PromptWithFields.vue (1)

159-174: 🛠️ Refactor suggestion

Add tooltip cleanup on component unmount

The tooltips initialized with tippy should be cleaned up when the component is unmounted to prevent memory leaks.

Consider storing the tooltip instances and destroying them in the onBeforeUnmount hook.

Apply this change to manage the tooltip instances:

+ const tooltipInstances: any[] = []

function loadMentionFieldTagTooltip() {
  document.querySelectorAll('.nc-ai-prompt-with-fields .prompt-field-tag').forEach((el) => {
    const tooltip = Object.values(el.attributes).find((attr) => attr.name === 'data-tooltip')
    if (!tooltip || el.scrollWidth <= el.clientWidth) return
    // Show tooltip only on truncate
-   tippy(el, {
+   const instance = tippy(el, {
      content: `<div class="tooltip text-xs">${tooltip.value}</div>`,
      placement: 'top',
      allowHTML: true,
      arrow: true,
      animation: 'fade',
      duration: 0,
      maxWidth: '200px',
    })
+   tooltipInstances.push(instance)
  })
}

+ onBeforeUnmount(() => {
+   tooltipInstances.forEach((instance) => instance.destroy())
+   tooltipInstances.length = 0
+ })
packages/nc-gui/components/ai/Settings.vue (2)

24-28: ⚠️ Potential issue

Fix the condition in isAiIntegrationAvailableInList.

The function has a bug where it's using the computed property name instead of its value.

const isAiIntegrationAvailableInList = (integrationId?: string) => {
-  if (!aiIntegrationAvailable) return false
+  if (!aiIntegrationAvailable.value) return false

  return ncIsArrayIncludes(aiIntegrations.value, integrationId, 'id')
}

Likely invalid or redundant comment.


70-72: 🛠️ Refactor suggestion

Add cleanup in early return.

When returning early due to missing data, ensure generatingPreview is reset.

if (!formattedData.value.length || !pk) {
  message.error('Include at least 1 sample record in table to generate')
+ try {
  generatingPreview.value = false
+ } finally {
+   // Ensure cleanup happens even if error occurs
+   generatingPreview.value = false
+ }
  return
}

Likely invalid or redundant comment.

packages/nc-gui/composables/useNocoAi.ts (1)

20-21: Consider adding migration strategy for type change.

The type change from IntegrationType[] to Partial<IntegrationType>[] might affect existing code that expects all properties to be present.

packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

48-58: ⚠️ Potential issue

Initialize vModel.value.meta before assignment.

The setter of isEnabledGenerateText might throw if vModel.value.meta is undefined.

set: (value: boolean) => {
+  if (!vModel.value.meta) vModel.value.meta = {}
  vModel.value.meta[LongTextAiMetaProp] = value
  vModel.value.prompt_raw = ''
  previewRow.value.row = {}
  isAlreadyGenerated.value = false
},

Likely invalid or redundant comment.


66-104: ⚠️ Potential issue

Add error handling for async operations.

The generate function should handle errors from both loadViewData and generateRows.

const generate = async () => {
+ try {
    generatingPreview.value = true
    await loadViewData()
    
    const pk = formattedData.value.length 
      ? extractPkFromRow(unref(formattedData.value[0].row), meta.value?.columns || []) 
      : ''

    if (!formattedData.value.length || !pk) {
      message.error('Include at least 1 sample record in table to generate')
      return
    }

    previewFieldTitle.value = vModel.value?.title || 'temp_title'
    const res = await generateRows(/* ... */)
    
    if (res?.length && res[0]?.[previewFieldTitle.value]) {
      previewRow.value.row = {/* ... */}
      isAlreadyGenerated.value = true
    }
+ } catch (error) {
+   message.error(`Failed to generate: ${error.message}`)
+ } finally {
+   generatingPreview.value = false
+ }
- generatingPreview.value = false
}

Likely invalid or redundant comment.

packages/nocodb/src/models/Integration.ts (2)

454-457: LGTM: Defensive initialization of config.models array

The initialization of config.models as an empty array when it's not already an array is a good defensive programming practice that prevents potential null reference errors.


474-500: 🛠️ Refactor suggestion

Refactor duplicate unbinding logic

The unbinding logic is duplicated between delete and softDelete methods. Consider extracting it into a separate private method to improve maintainability.

Apply this diff to reduce code duplication:

+ private async unbindAssociations(ncMeta = Noco.ncMeta) {
+   // unbind all buttons and long texts associated with this integration
+   await ncMeta.metaUpdate(
+     this.fk_workspace_id ? this.fk_workspace_id : RootScopes.WORKSPACE,
+     RootScopes.WORKSPACE,
+     MetaTable.COL_BUTTON,
+     {
+       fk_integration_id: null,
+       model: null,
+     },
+     {
+       fk_integration_id: this.id,
+     },
+   );
+
+   await ncMeta.metaUpdate(
+     this.fk_workspace_id ? this.fk_workspace_id : RootScopes.WORKSPACE,
+     RootScopes.WORKSPACE,
+     MetaTable.COL_LONG_TEXT,
+     {
+       fk_integration_id: null,
+       model: null,
+     },
+     {
+       fk_integration_id: this.id,
+     },
+   );
+ }

  async delete(ncMeta = Noco.ncMeta) {
    const sources = await this.getSources(ncMeta, true);
    for (const source of sources) {
      await source.delete(
        {
          workspace_id: this.fk_workspace_id,
          base_id: source.base_id,
        },
        ncMeta,
      );
    }

-   // unbind all buttons and long texts associated with this integration
-   await ncMeta.metaUpdate(...)
-   await ncMeta.metaUpdate(...)
+   await this.unbindAssociations(ncMeta);

    return await ncMeta.metaDelete(...);
  }

  async softDelete(ncMeta = Noco.ncMeta) {
    const sources = await this.getSources(ncMeta, true);
    for (const source of sources) {
      await source.softDelete(...);
    }

-   // unbind all buttons and long texts associated with this integration
-   await ncMeta.metaUpdate(...)
-   await ncMeta.metaUpdate(...)
+   await this.unbindAssociations(ncMeta);

    await ncMeta.metaUpdate(...);
  }

Also applies to: 522-547

packages/nc-gui/components/cell/TextArea.vue (3)

17-18: 🛠️ Refactor suggestion

Consider using a default value for meta injection

The injected meta dependency uses ref() as a fallback, which could lead to runtime errors when accessing properties. Consider providing a more specific default value.

-const meta = inject(MetaInj, ref())
+const meta = inject(MetaInj, ref({ columns: [] }))

100-103: ⚠️ Potential issue

Avoid using non-null assertions to prevent potential runtime errors

The use of non-null assertions ! in meta.value!.columns! may lead to runtime errors if meta.value or columns are undefined. Consider adding null checks or default values to ensure the code is robust against null or undefined values.

Apply this diff to handle possible null values:

const rowId = computed(() => {
-  return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
+  if (currentRow.value?.row && meta.value?.columns) {
+    return extractPkFromRow(currentRow.value.row, meta.value.columns)
+  }
+  return null
})

542-545: ⚠️ Potential issue

Correct the usage of Tailwind CSS classes

The class animate-infinite is not a valid Tailwind CSS utility. The animate-spin class already provides an infinite spin animation. Remove animate-infinite to prevent any unexpected behavior.

Apply this diff to correct the class:

<GeneralIcon
  icon="refresh"
  class="transform group-hover:(!text-grey-800) text-gray-700 w-3 h-3"
-  :class="{ 'animate-infinite animate-spin': isAiGenerating }"
+  :class="{ 'animate-spin': isAiGenerating }"
/>
packages/nc-gui/components/smartsheet/expanded-form/index.vue (2)

330-333: LGTM: Improved user feedback for URL copying

The addition of a delay after copying the URL provides better visual feedback to users. The 5-second timeout is a reasonable duration for the copy confirmation message.


Line range hint 337-353: LGTM: Enhanced modal state handling

The saveChanges method now properly handles both prevent change and close modal states, ensuring consistent behavior when saving changes.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (2)

186-196: LGTM: Proper handling of AI prompt visibility in edit mode

The condition hideAiPromptOnEdit correctly prevents AI prompt and button types from being shown in edit mode, which is the expected behavior since these types cannot be changed after creation.


Line range hint 263-285: LGTM: Comprehensive handling of AI prompt type selection

The onSelectType function properly handles AI prompts by:

  1. Setting the correct underlying type (LongText)
  2. Initializing required metadata
  3. Preserving AI-specific properties
packages/nocodb/src/db/conditionV2.ts (1)

596-613: ⚠️ Potential issue

Add error handling for JSON extraction

The JSON extraction logic should handle invalid JSON values to prevent runtime errors.

Apply this diff to add error handling:

if (isAIPromptCol(column)) {
+ const handleInvalidJson = (field) => {
+   return knex.raw('CASE WHEN JSON_VALID(??) THEN ? ELSE NULL END', [
+     column.column_name,
+     field
+   ]);
+ };

  if (knex.clientType() === 'pg') {
-   field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name])
+   );
  } else if (knex.clientType().startsWith('mysql')) {
-   field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name])
+   );
  } // Apply similar changes for other DB types
}
packages/nocodb/src/models/Column.ts (2)

452-469: ⚠️ Potential issue

Add error handling for AI column insertion

The AI column insertion lacks error handling.

Apply this diff to add error handling:

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   try {
      await AIColumn.insert(
        context,
        {
          fk_model_id: column.fk_model_id,
          fk_column_id: colId,
          fk_integration_id: column.fk_integration_id,
          model: column.model,
          prompt: column.prompt,
          prompt_raw: column.prompt_raw,
          error: column.error,
        },
        ncMeta,
      );
+   } catch (error) {
+     logger.error(`Failed to insert AI column: ${error.message}`);
+     throw error;
+   }
  }
  break;
}

886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect.

Apply this diff to fix the placeholder matching:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (1)

182-182: LGTM: AI prompt column filtering

The filtering logic correctly excludes AI prompt columns from the initial import, which is necessary since they require special handling.

packages/nc-gui/components/smartsheet/details/Fields.vue (1)

1040-1043: ⚠️ Potential issue

Fix undefined ncDelay function

The ncDelay function is not defined or imported, which will lead to a runtime error. Use the delay function from @vueuse/core instead.

+import { delay } from '@vueuse/core'

await copy(field.id!)
isFieldIdCopied.value = true
-await ncDelay(5000)
+await delay(5000)
isFieldIdCopied.value = false

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (1)

1027-1028: LGTM: AI prompt column check

The isSelectedOnlyAI computed property correctly checks for both AI prompt columns and AI buttons.

packages/nc-gui/components/smartsheet/grid/Table.vue (1)

855-856: Enhance AI column type checking

The condition now properly checks for both AI prompt columns and AI button columns, improving type safety.

packages/nocodb/src/services/columns.service.ts (3)

2282-2304: ⚠️ Potential issue

Add AI prompt handling during column creation

The code processes AI prompt templates during column creation.

The prompt template processing needs additional security checks as mentioned in the previous comment.

Add validation for AI integration requirements:

if (isAIPromptCol(colBody)) {
+ // Validate AI integration requirements  
+ if (!colBody.meta?.aiEnabled) {
+   NcError.badRequest('AI integration must be enabled for AI prompt columns');
+ }

+ // Validate AI model configuration
+ if (!colBody.meta?.aiModel) {
+   NcError.badRequest('AI model must be specified for AI prompt columns');
+ }

  let prompt = '';
  // Rest of the code...
}

1601-1621: ⚠️ Potential issue

Add prompt template processing

The code processes the prompt template by replacing column aliases with their IDs.

The prompt template processing needs additional security checks:

  1. Validate template complexity to prevent DoS attacks
  2. Sanitize input to prevent injection attacks
  if (colBody.prompt_raw) {
    await table.getColumns(context);

+   // Validate template complexity
+   const MAX_TEMPLATE_LENGTH = 10000;
+   const MAX_PLACEHOLDERS = 50;
+   if (colBody.prompt_raw.length > MAX_TEMPLATE_LENGTH) {
+     NcError.badRequest('Prompt template too long');
+   }
+   if ((colBody.prompt_raw.match(/{/g) || []).length > MAX_PLACEHOLDERS) {
+     NcError.badRequest('Too many placeholders in template');
+   }

+   // Validate template format
+   const validTemplatePattern = /^[^{}]*(\{[^{}]+\}[^{}]*)*$/;
+   if (!validTemplatePattern.test(colBody.prompt_raw)) {
+     NcError.badRequest('Invalid prompt template format');
+   }

    prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
      const column = table.columns.find((c) => c.title === p1);

      if (!column) {
        NcError.badRequest(`Field '${p1}' not found`);
      }

+     // Sanitize column reference
+     if (!/^[a-zA-Z0-9_]+$/.test(p1)) {
+       NcError.badRequest('Invalid field name format');
+     }

      return `{${column.id}}`;
    });
  }

Likely invalid or redundant comment.


1578-1600: Add data conversion for AI column type changes

The code handles converting data from AI format to regular format when changing column type.

Run the following script to verify the data conversion:

✅ Verification successful

Let me gather more information about the data conversion implementation to verify it properly.


Let me try a different approach to get the implementation details.


Let me get more context about the data conversion implementation.


Data conversion for AI columns properly implemented

The code correctly handles bidirectional data conversion between AI and non-AI formats across all supported databases (PostgreSQL, MySQL, SQLite, and MS SQL):

  • convertAIRecordTypeToValue: Extracts the value from JSON object when converting from AI to regular format
  • convertValueToAIRecordType: Wraps the value in a JSON object with metadata when converting to AI format

The implementation includes proper null checks and database-specific JSON handling functions for each database type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify data conversion from AI to non-AI format
# Test: Search for any AI prompt columns that were converted
rg -A 5 "convertAIRecordTypeToValue|convertValueToAIRecordType"

Length of output: 2279


Script:

#!/bin/bash
# Check the implementation of data conversion functions
ast-grep --pattern 'export const convertAIRecordTypeToValue = async $_'
ast-grep --pattern 'export const convertValueToAIRecordType = async $_'

Length of output: 65856


Script:

#!/bin/bash
# Search for the data conversion implementation files
rg -g "*.ts" -A 10 "export const convertAIRecordTypeToValue" packages/nocodb/src/utils/

Length of output: 864


Script:

#!/bin/bash
# Get the full implementation of both conversion functions
cat packages/nocodb/src/utils/dataConversion.ts

Length of output: 4846

packages/nocodb/src/db/BaseModelSqlv2.ts (3)

10062-10137: LGTM! Well-structured AI prompt column handling

The implementation properly handles:

  • AI field value storage and updates
  • Last modified tracking with user attribution
  • Staleness state management based on referenced column changes
  • Different behavior for system vs user updates

5721-5723: ⚠️ Potential issue

Fix async operation handling in bulkInsert

The current implementation doesn't properly await all prepareNocoData operations. This could lead to race conditions or data inconsistency.

Apply this diff to properly handle async operations:

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.


8635-8653: 🛠️ Refactor suggestion

Improve error handling for JSON parsing

The JSON parsing logic has minimal error handling with empty catch blocks that could hide important errors. Additionally, there's no validation of the parsed data structure.

Apply this diff to improve error handling:

if (d) {
  for (const col of jsonColumns) {
    if (d[col.id] && typeof d[col.id] === 'string') {
      try {
        d[col.id] = JSON.parse(d[col.id]);
+       // Validate expected data structure
+       if (typeof d[col.id] !== 'object') {
+         throw new Error('Invalid JSON structure');
+       }
-     } catch {}
+     } catch (e) {
+       logger.warn(`Error parsing JSON for column ${col.id}: ${e.message}`);
+       d[col.id] = null; // Set to null on parse error
+     }
    }

    if (d[col.id]?.length) {
      for (let i = 0; i < d[col.id].length; i++) {
        if (typeof d[col.id][i] === 'string') {
          try {
            d[col.id][i] = JSON.parse(d[col.id][i]);
+           // Validate array element structure
+           if (typeof d[col.id][i] !== 'object') {
+             throw new Error('Invalid JSON array element');
+           }
-         } catch {}
+         } catch (e) {
+           logger.warn(`Error parsing JSON array element: ${e.message}`);
+           d[col.id][i] = null;
+         }
        }
      }
    }
  }
}

Likely invalid or redundant comment.

Comment on lines 66 to 67
o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
) ?? []
Copy link
Contributor

Choose a reason for hiding this comment

10000

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

⚠️ Potential issue

Correct the string comparison logic in the suggestion filter

There seems to be an extra closing brace '}' in the string comparison, which may lead to incorrect filtering of suggestions.

Update the comparison to ensure accurate matching of the query with the option title.

Apply this diff to fix the issue:

return (
  props.options?.filter(
    (o) =>
-     o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
+     o.title?.toLowerCase()?.includes(query.toLowerCase()) || `{${o.title?.toLowerCase()}}` === query.toLowerCase(),
  ) ?? []
)

This change ensures that when the user types an exact field placeholder (e.g., {fieldname}), it matches the options correctly.

📝 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.

Suggested change
o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
) ?? []
o.title?.toLowerCase()?.includes(query.toLowerCase()) || `{${o.title?.toLowerCase()}}` === query.toLowerCase(),
) ?? []

Comment on lines 1961 to 1974
case UITypes.LongText: {
if (column.meta?.[LongTextAiMetaProp] === true) {
insertArr.push({
fk_model_id: column.fk_model_id,
fk_column_id: column.id,
fk_integration_id: column.fk_integration_id,
model: column.model,
prompt: column.prompt,
prompt_raw: column.prompt_raw,
error: column.error,
});
}
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for required AI column fields

The bulk insert for AI columns should validate required fields before insertion.

Add validation:

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   const requiredFields = ['fk_model_id', 'fk_column_id', 'model'];
+   const missingFields = requiredFields.filter(field => !column[field]);
+   if (missingFields.length > 0) {
+     throw new Error(`Missing required fields for AI column: ${missingFields.join(', ')}`);
+   }
    insertArr.push({
      fk_model_id: column.fk_model_id,
      fk_column_id: column.id,
      fk_integration_id: column.fk_integration_id,
      model: column.model,
      prompt: column.prompt,
      prompt_raw: column.prompt_raw,
      error: column.error,
    });
  }
  break;
}
📝 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.

Suggested change
case UITypes.LongText: {
if (column.meta?.[LongTextAiMetaProp] === true) {
insertArr.push({
fk_model_id: column.fk_model_id,
fk_column_id: column.id,
fk_integration_id: column.fk_integration_id,
model: column.model,
prompt: column.prompt,
prompt_raw: column.prompt_raw,
error: column.error,
});
}
break;
}
case UITypes.LongText: {
if (column.meta?.[LongTextAiMetaProp] === true) {
const requiredFields = ['fk_model_id', 'fk_column_id', 'model'];
const missingFields = requiredFields.filter(field => !column[field]);
if (missingFields.length > 0) {
throw new Error(`Missing required fields for AI column: ${missingFields.join(', ')}`);
}
insertArr.push({
fk_model_id: column.fk_model_id,
fk_column_id: column.id,
fk_integration_id: column.fk_integration_id,
model: column.model,
prompt: column.prompt,
prompt_raw: column.prompt_raw,
error: column.error,
});
}
break;
}

Comment on lines 1113 to 1114
a.uidt === UITypes.Barcode ||
isAIPromptCol(a)) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation and error handling for AI prompt column import

The AI prompt column import lacks validation and error handling. Consider adding checks for required fields and error handling.

} else if (isAIPromptCol(col)) {
  if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
    colOptions.fk_workspace_id = null;
    colOptions.fk_integration_id = null;
    colOptions.model = null;
  }

+ // Validate required fields
+ if (!colOptions.prompt_raw) {
+   this.logger.warn(`Skipping AI prompt column import: Missing prompt_raw for column ${col.title}`);
+   continue;
+ }

+ try {
    const freshModelData = await this.columnsService.columnAdd(context, {
      tableId: getIdOrExternalId(getParentIdentifier(col.id)),
      column: withoutId({
        ...flatCol,
        ...{
          fk_integration_id: colOptions.fk_integration_id,
          model: colOptions.model,
          prompt_raw: colOptions.prompt_raw,
        },
      }) as any,
      req: param.req,
      user: param.user,
    });
+ } catch (error) {
+   this.logger.error(`Failed to import AI prompt column ${col.title}: ${error.message}`);
+   throw error;
+ }

Also applies to: 1281-1296

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch 2 times, most recently from e7a3f81 to 126223b Compare December 7, 2024 10:16
Copy link
Contributor
@coderabbitai coderabbitai bot left a 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

🧹 Outside diff range and nitpick comments (6)
packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2)

Line range hint 174-189: Consider enhancing the error message for formula_raw validation

The validation logic is well-structured, but the error message for formula_raw could be more descriptive to better guide users.

 formula_raw: [
   {
     required: true,
-    message: 'Prompt required for AI Button',
+    message: 'Please provide a prompt template for the AI Button to generate data',
   },
 ],

Line range hint 212-216: Consider extracting default values into constants

The default values for AI button type should be extracted into named constants at the top of the file for better maintainability and reusability.

+const AI_BUTTON_DEFAULTS = {
+  THEME: 'light',
+  LABEL: 'Generate data',
+  COLOR: 'purple',
+  ICON: 'ncAutoAwesome',
+} as const

 if (vModel.value.type === ButtonActionsType.Ai) {
-  vModel.value.theme = 'light'
-  vModel.value.label = 'Generate data'
-  vModel.value.color = 'purple'
-  vModel.value.icon = 'ncAutoAwesome'
+  vModel.value.theme = AI_BUTTON_DEFAULTS.THEME
+  vModel.value.label = AI_BUTTON_DEFAULTS.LABEL
+  vModel.value.color = AI_BUTTON_DEFAULTS.COLOR
+  vModel.value.icon = AI_BUTTON_DEFAULTS.ICON
   vModel.value.output_column_ids = ''
 }
packages/nc-gui/composables/useNocoAi.ts (1)

Line range hint 1-367: Consider improving error handling consistency

The file contains multiple functions with similar error handling patterns. Consider:

  1. Centralizing error messages into constants
  2. Creating a common error handling utility function
  3. Standardizing the error response format across all AI operations

This would improve maintainability and ensure consistent error handling throughout the codebase.

packages/nc-gui/composables/useIntegrationsStore.ts (2)

Line range hint 447-496: Consider breaking down the loadDynamicIntegrations function

The function handles multiple responsibilities. Consider extracting the icon processing logic into a separate function for better maintainability.

+ const processIntegrationIcon = (meta: { icon?: string, title?: string }, subType: string) => {
+   if (!meta.icon) return iconMap.puzzle
+   
+   if (meta.icon in iconMap) {
+     return iconMap[meta.icon as keyof typeof iconMap]
+   }
+   
+   if (isValidURL(meta.icon)) {
+     return h('img', {
+       src: meta.icon,
+       alt: meta.title || subType,
+     })
+   }
+   
+   return iconMap.puzzle
+ }

const loadDynamicIntegrations = async () => {
  if (integrationsInitialized.value) return
  integrationsInitialized.value = true

  const dynamicIntegrations = (await $api.integrations.list()) as {
    type: IntegrationsType
    subType: string
    meta: {
      title?: string
      icon?: string
      description?: string
      order?: number
    }
  }[]

  dynamicIntegrations.sort((a, b) => (a.meta.order ?? Infinity) - (b.meta.order ?? Infinity))

  for (const di of dynamicIntegrations) {
-   let icon: FunctionalComponent<SVGAttributes, {}, any, {}> | VNode
-   if (di.meta.icon) {
-     if (di.meta.icon in iconMap) {
-       icon = iconMap[di.meta.icon as keyof typeof iconMap]
-     } else {
-       if (isValidURL(di.meta.icon)) {
-         icon = h('img', {
-           src: di.meta.icon,
-           alt: di.meta.title || di.subType,
-         })
-       }
-     }
-   } else {
-     icon = iconMap.puzzle
-   }
+   const icon = processIntegrationIcon(di.meta, di.subType)

    const integration: IntegrationItemType = {
      title: di.meta.title || di.subType,
      subType: di.subType,
      icon,
      type: di.type,
      isAvailable: true,
      dynamic: true,
    }

    allIntegrations.push(integration)
    integrationsRefreshKey.value++
  }
}

296-296: Use optional chaining for safer property access

As suggested by static analysis, use optional chaining to safely access potentially undefined properties.

- if (response && response?.id) {
+ if (response?.id) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nc-gui/components/smartsheet/grid/Table.vue (1)

Line range hint 879-910: Add error handling for output column parsing

While the changes add support for multiple output columns, consider adding validation and error handling:

  1. Validate that output column IDs exist in the table
  2. Handle potential parsing errors when splitting comma-separated IDs
  3. Add error handling for empty or invalid output column configurations
  if (isAiButton(field)) {
+   // Validate output column configuration
+   if (!field.colOptions?.output_column_ids) {
+     NcError.badRequest('Output columns not configured for AI button');
+   }
+
+   try {
      outputColumnIds =
        ncIsString(field.colOptions?.output_column_ids) && field.colOptions.output_column_ids.split(',').length > 0
          ? field.colOptions.output_column_ids.split(',')
          : [];
+
+     // Validate that output columns exist
+     for (const colId of outputColumnIds) {
+       if (!meta.value?.columns?.find(c => c.id === colId)) {
+         NcError.badRequest(`Output column ${colId} not found in table`);
+       }
+     }
+   } catch (e) {
+     NcError.badRequest('Invalid output column configuration');
+   }
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c2d7348 and 126223b.

📒 Files selected for processing (70)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (11 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (43)
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nocodb/src/models/index.ts
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/utils/commonUtils.ts
  • packages/nocodb-sdk/src/lib/helperFunctions.ts
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nocodb-sdk/src/lib/UITypes.ts
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nocodb/src/models/Integration.ts
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/components/smartsheet/header/Menu.vue
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/cell/attachment/index.vue
  • packages/nc-gui/components/ai/Settings.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nc-gui/components/general/Loader.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nc-gui/components/ai/PromptWithFields.vue
  • packages/nc-gui/components/smartsheet/grid/GroupByTable.vue
  • packages/nc-gui/utils/columnUtils.ts
🧰 Additional context used
📓 Learnings (1)
packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (1)
Learnt from: DarkPhoenix2704
PR: nocodb/nocodb#8781
File: packages/nc-gui/components/smartsheet/grid/GroupBy.vue:225-232
Timestamp: 2024-11-12T03:23:04.401Z
Learning: DarkPhoenix2704 prefers to keep code implementations that prioritize readability, even if an alternative might be more concise.
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (52)
packages/nc-gui/components/smartsheet/Kanban.vue (2)

1146-1146: LGTM: Icon update for consistency

The icon change from expand to maximize aligns with similar updates in other components, maintaining UI consistency across the application.


Line range hint 1-1146: LGTM: Clean implementation of empty stack visibility

The hideEmptyStack computed property is well-implemented, providing a clean way to toggle empty stack visibility with proper default handling.

packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: 🛠️ Refactor suggestion

Fix undefined ncDelay function and improve user feedback timing

  1. The ncDelay function is still undefined and needs to be imported.

  2. The implementation can be improved:

  • 5 seconds might be too long for user feedback
  • Error handling should be added

Consider this implementation:

-  await ncDelay(5000)
+  try {
+    // Use setTimeout for a shorter 2-second feedback
+    await new Promise((resolve) => setTimeout(resolve, 2000))
+  } catch (e) {
+    console.error('Error in copy feedback timer:', e)
+  } finally {
+    isViewIdCopied.value = false
+  }

Alternatively, if you prefer using the utility function:

+import { ncDelay } from '#imports'
// ... rest of the imports

-  await ncDelay(5000)
+  try {
+    // Reduce to 2 seconds for better UX
+    await ncDelay(2000)
+  } catch (e) {
+    console.error('Error in copy feedback timer:', e)
+  } finally {
+    isViewIdCopied.value = false
+  }
packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (4)

Line range hint 1-7: LGTM: Clean import addition

The addition of isAIPromptCol import is clean and follows the existing pattern of importing type checks from nocodb-sdk.


181-183: LGTM: Proper column filtering

The addition of !isAIPromptCol(a) in the column filtering logic is appropriate, ensuring AI prompt columns are handled separately like other special column types.


1113-1114: LGTM: Consistent column type handling

The addition of isAIPromptCol(a) to the referenced column set check is consistent with how other special column types are handled.


1281-1296: Add validation for AI prompt column import

The AI prompt column import implementation follows the same pattern as button columns but lacks validation for required fields.

This was previously noted in a review comment. The suggestion to add validation for required fields and error handling remains valid.

packages/nc-gui/composables/useNocoAi.ts (4)

20-21: LGTM: Type change improves flexibility

The change from IntegrationType[] to Partial<IntegrationType>[] is a good improvement that allows for more flexible integration definitions where not all fields are required.


22-23: LGTM: Clean computed property implementation

The computed property provides a clean way to determine AI integration availability based on the array length. This is an improvement over manual state management.


347-347: LGTM: Proper method export

The new method is correctly exported in the composable's return object.


24-28: ⚠️ Potential issue

Fix computed property access and missing import

There are two issues in this method:

  1. The computed property aiIntegrationAvailable is used without accessing its .value
  2. The ncIsArrayIncludes helper function appears to be used without being imported
+ import { ncIsArrayIncludes } from '<path-to-helper>'
  
  const isAiIntegrationAvailableInList = (integrationId?: string) => {
-   if (!aiIntegrationAvailable) return false
+   if (!aiIntegrationAvailable.value) return false
    return ncIsArrayIncludes(aiIntegrations.value, integrationId, 'id')
  }
packages/nc-gui/composables/useIntegrationsStore.ts (3)

93-94: LGTM: Clean integration with AI-specific state management

Good separation of concerns by using a dedicated composable for AI integrations.


191-192: LGTM: Comprehensive event tracking implementation

Event tracking has been consistently implemented across all major integration operations, providing valuable analytics data.

Also applies to: 224-225, 253-254, 287-291


229-238: ⚠️ Potential issue

Avoid mutating objects within map operations

The current implementation mutates objects within map operations, which can lead to reactivity issues in Vue.js. Return new objects instead.

Apply these fixes:

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.title = integration.title
-     return i
+     return { ...i, title: integration.title }
    }
    return i
  })
}
if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => 
    i.id === integration.id
      ? { ...i, is_default: true }
      : { ...i, is_default: false }
  )
}

Also applies to: 258-269

packages/nocodb/src/db/BaseModelSqlv2.ts (3)

10071-10075: ⚠️ Potential issue

Correct the type of isStale to boolean

The isStale property is used as a boolean in the code. Currently, it's typed as string?, which can lead to type mismatches. It should be typed as boolean? to reflect its usage.

Apply this diff to fix the type:

const obj: {
  value?: string;
  lastModifiedBy?: string;
  lastModifiedTime?: string;
- isStale?: string;
+ isStale?: boolean;
} = {};

Likely invalid or redundant comment.


5721-5723: ⚠️ Potential issue

Ensure prepareNocoData is properly awaited inside map

The prepareNocoData function is asynchronous, but its calls inside insertDatas.map are not being awaited. This can lead to unhandled Promise rejections and unexpected behavior.

Apply this diff to fix the issue:

- insertDatas.map((d) =>
-   this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ await Promise.all(
+   insertDatas.map((d) =>
+     this.prepareNocoData(d, true, cookie, null, { raw }),
+   ),
+ );

Likely invalid or redundant comment.


8635-8653: 🛠️ Refactor suggestion

Improve error handling for JSON parsing

The JSON parsing logic has minimal error handling with empty catch blocks that could hide important errors. Additionally, there's no validation of the parsed data structure.

Apply this diff to improve error handling:

if (d) {
  for (const col of jsonColumns) {
    if (d[col.id] && typeof d[col.id] === 'string') {
      try {
        d[col.id] = JSON.parse(d[col.id]);
+       // Validate expected data structure
+       if (typeof d[col.id] !== 'object') {
+         throw new Error('Invalid JSON structure');
+       }
-     } catch {}
+     } catch (e) {
+       logger.warn(`Error parsing JSON for column ${col.id}: ${e.message}`);
+       d[col.id] = null; // Set to null on parse error
+     }
    }

    if (d[col.id]?.length) {
      for (let i = 0; i < d[col.id].length; i++) {
        if (typeof d[col.id][i] === 'string') {
          try {
            d[col.id][i] = JSON.parse(d[col.id][i]);
+           // Validate array element structure
+           if (typeof d[col.id][i] !== 'object') {
+             throw new Error('Invalid JSON array element');
+           }
-         } catch {}
+         } catch (e) {
+           logger.warn(`Error parsing JSON array element: ${e.message}`);
+           d[col.id][i] = null;
+         }
        }
      }
    }
  }
}

Likely invalid or redundant comment.

packages/nocodb/src/models/AIColumn.ts (3)

14-16: Ensure correct instantiation in the castType method

The castType method might return undefined when data is falsy, which could lead to unexpected behavior. It's better to explicitly handle falsy values to maintain consistent return types.

Consider changing the return statement to:

-  return data && new AIColumn(data);
+  return data ? new AIColumn(data) : null;

26-26: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. Referencing the class name directly improves readability.

Apply this diff to fix the issue:

-        return this._insert(
+        return AIColumn._insert(
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. Referencing the class name directly improves readability.

Apply this diff to fix the issue:

-        return this._update(
+        return AIColumn._update(
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. Referencing the class name directly improves readability.

Apply this diff to fix the issue:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. Referencing the class name directly improves readability.

Apply this diff to fix the issue:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

135-149: ⚠️ Potential issue

Avoid string concatenation in SQL queries to prevent SQL injection

In the MSSQL query, string concatenation is used to build the JSON string, which can lead to SQL injection vulnerabilities. Even when using parameter bindings, concatenating SQL strings can introduce security risks.

Consider constructing the JSON object in code and passing it as a parameter to the SQL query. This approach avoids string concatenation within the SQL statement.

For example, you can construct the JSON string in JavaScript and then use JSON_QUERY(?) in your SQL statement:

-        await sqlClient.raw(
-          `UPDATE ??
-            SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}')
-            WHERE ?? is not null;`,
-          [
-       
10000
     baseModel.getTnPath(table.table_name),
-            column.column_name,
-            column.column_name,
-            commonRecord.lastModifiedBy.toString(),
-            commonRecord.lastModifiedTime.toString(),
-            commonRecord.isStale,
-            column.column_name,
-          ],
-        );
+        const jsonData = JSON.stringify({
+          value: sqlClient.raw('??', [column.column_name]),
+          lastModifiedBy: commonRecord.lastModifiedBy.toString(),
+          lastModifiedTime: commonRecord.lastModifiedTime.toString(),
+          isStale: commonRecord.isStale,
+        });
+
+        await sqlClient.raw(
+          `UPDATE ??
+            SET ?? = JSON_QUERY(?)
+            WHERE ?? is not null;`,
+          [
+            baseModel.getTnPath(table.table_name),
+            column.column_name,
+            jsonData,
+            column.column_name,
+          ],
+        );

This ensures that all parameters are properly escaped and reduces the risk of SQL injection.

Likely invalid or redundant comment.

packages/nc-gui/components/cell/AI.vue (2)

81-92: Consider adding error handling for AI response processing

The code processes AI responses without explicit error handling. If the response format is unexpected, it could lead to runtime errors.

Consider adding try-catch blocks to handle potential errors:

          if (obj && typeof obj === 'object') {
+           try {
              vModel.value = obj
              setTimeout(() => {
                isAiEdited.value = false
              }, 100)
+           } catch (error) {
+             console.error('Error processing AI response:', error)
+             // Optionally, show a user-friendly error message to the user
+           }
          } else {
+           try {
              vModel.value = {
                ...(ncIsObject(vModel.value) ? vModel.value : {}),
                isStale: false,
                value: resRow[column.value.title!],
              }
+           } catch (error) {
+             console.error('Error setting AI response value:', error)
+             // Optionally, show a user-friendly error message to the user
+           }
          }

131-135: Fix the tooltip disabled logic

The :disabled prop on NcTooltip seems to have incorrect logic. The tooltip should be disabled when the button is enabled, not when it's disabled.

Consider updating the :disabled prop to reflect the inverse of isDisabledAiButton:

-        <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+        <NcTooltip :disabled="!isDisabledAiButton" class="flex">

This change ensures the tooltip is enabled when the button is disabled, providing users with feedback on why the button is not clickable.

packages/nocodb/src/db/sortV2.ts (1)

203-221: Remove duplicate condition check

The isAIPromptCol(column) condition is checked twice unnecessarily within the same block. This redundancy can be removed to simplify the code.

Apply this fix to eliminate the duplicate condition:

          case UITypes.LongText: {
            if (isAIPromptCol(column)) {
              let col;
-             if (isAIPromptCol(column)) {
                if (knex.clientType() === 'pg') {
                  col = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [
                    column.column_name,
                  ]);
                } else if (knex.clientType().startsWith('mysql')) {
                  col = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [
                    column.column_name,
                  ]);
                } else if (knex.clientType() === 'sqlite3') {
                  col = knex.raw(`json_extract(??, '$.value')`, [
                    column.column_name,
                  ]);
                } else if (knex.clientType() === 'mssql') {
                  col = knex.raw(`JSON_VALUE(??, '$.value')`, [column.column_name]);
                }
-             }
              qb.orderBy(col, sort.direction || 'asc', nulls);
            } else {
              qb.orderBy(
                sanitize(column.column_name),
                sort.direction || 'asc',
                nulls,
              );
            }
            break;
          }
packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (4)

1-48: Well-structured component initialization!

The component follows Vue 3 best practices with proper TypeScript integration, clean event handling, and efficient computed properties.


170-324: Well-structured template with proper accessibility!

The template section follows Vue best practices with clean conditional rendering and proper use of UI components.


48-58: ⚠️ Potential issue

Add null check for meta property

Direct access to vModel.value.meta without a null check could cause runtime errors.

Apply this diff to add the necessary null check:

set: (value: boolean) => {
+  if (!vModel.value.meta) vModel.value.meta = {}
  vModel.value.meta[LongTextAiMetaProp] = value
  vModel.value.prompt_raw = ''
  previewRow.value.row = {}
  isAlreadyGenerated.value = false
},

Likely invalid or redundant comment.


66-104: ⚠️ Potential issue

Add error handling for generate function

The generate function makes an API call without error handling, which could lead to unhandled promise rejections.

Apply this diff to add error handling:

const generate = async () => {
  generatingPreview.value = true

  await loadViewData()

  const pk = formattedData.value.length ? extractPkFromRow(unref(formattedData.value[0].row), meta.value?.columns || []) : ''

  if (!formattedData.value.length || !pk) {
    message.error('Include at least 1 sample record in table to generate')
    generatingPreview.value = false
    return
  }

  previewFieldTitle.value = vModel.value?.title || 'temp_title'

+ try {
    const res = await generateRows(
      meta.value.id!,
      {
        title: previewFieldTitle.value,
        prompt_raw: vModel.value.prompt_raw,
        fk_integration_id: vModel.value.fk_integration_id,
        uidt: UITypes.LongText,
      },
      [pk],
    )

    if (res?.length && res[0]?.[previewFieldTitle.value]) {
      previewRow.value.row = {
        ...res[0],
        [previewFieldTitle.value]: {
          value: res[0]?.[previewFieldTitle.value],
        },
      }
      isAlreadyGenerated.value = true
    }
+ } catch (error: any) {
+   message.error(`Failed to generate text: ${error.message}`)
+ } finally {
    generatingPreview.value = false
+ }
}

Likely invalid or redundant comment.

packages/nc-gui/components/cell/TextArea.vue (3)

Line range hint 426-439: Well-implemented AI warning message section!

The warning message implementation provides clear user feedback with proper styling and iconography.


541-545: ⚠️ Potential issue

Fix invalid Tailwind animation class

The class animate-infinite is not a valid Tailwind CSS utility. The animate-spin class already provides an infinite animation.

Apply this diff to fix the animation:

<GeneralIcon
  icon="refresh"
  class="transform group-hover:(!text-grey-800) text-gray-700 w-3 h-3"
-  :class="{ 'animate-infinite animate-spin': isAiGenerating }"
+  :class="{ 'animate-spin': isAiGenerating }"
/>

Likely invalid or redundant comment.


100-111: ⚠️ Potential issue

Replace non-null assertions with safe null checks

The use of non-null assertions (!) in computed properties could lead to runtime errors if the values are undefined.

Apply this diff to add proper null checks:

const rowId = computed(() => {
-  return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
+  if (!currentRow.value?.row || !meta.value?.columns) return null
+  return extractPkFromRow(currentRow.value.row, meta.value.columns)
})

const isAiGenerating = computed(() => {
  return !!(
    rowId.value &&
    column?.value?.id &&
    generatingRows.value?.includes(rowId.value) &&
    generatingColumnRows.value?.includes(column.value.id)
  )
})

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/expanded-form/index.vue (1)

330-333: Clean implementation of URL copy feedback!

The implementation properly handles the copy feedback state with a reasonable timeout duration.

packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (2)

369-369: LGTM: Added isEdit prop to AiSettings component

The prop addition allows proper configuration based on edit mode state.


471-475: LGTM: Added tooltips for output field titles

The tooltips improve UX by showing full field titles when truncated.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (4)

186-196: LGTM: Added AIPrompt handling in UI filters

The hideAiPromptOnEdit condition correctly prevents editing AI prompt columns in edit mode.


Line range hint 263-285: LGTM: Enhanced onSelectType with AIPrompt support

The function now properly handles AIPrompt type by setting the correct UIType and metadata.


625-627: LGTM: Added isAiPromptSelectOption helper

Clean implementation of the helper method to check if a column is an AI prompt.


1098-1104: LGTM: Added dynamic icon handling for AI columns

The icon selection logic properly handles both AI button and AI prompt types.

packages/nocodb/src/db/conditionV2.ts (2)

4-4: LGTM: Added isAIPromptCol import

Clean import of the utility function from nocodb-sdk.


596-613: ⚠️ Potential issue

Add error handling for JSON extraction

The JSON extraction logic should handle invalid JSON values to prevent runtime errors.

Add error handling with this diff:

if (isAIPromptCol(column)) {
+ const handleInvalidJson = (field) => {
+   return knex.raw('CASE WHEN JSON_VALID(??) THEN ? ELSE NULL END', [
+     column.column_name,
+     field
+   ]);
+ };

  if (knex.clientType() === 'pg') {
-   field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name])
+   );
  } else if (knex.clientType().startsWith('mysql')) {
-   field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name])
+   );
  } // Apply similar changes for other DB types
}

Likely invalid or redundant comment.

packages/nocodb/src/models/Column.ts (4)

452-469: LGTM! Clean implementation of AI column insertion

The AIColumn insertion logic is well-structured and follows the pattern used for other column types.


573-577: LGTM! Clean implementation of AI column options retrieval

The getColOptions implementation for AI columns is clean and consistent with other column types.


1961-1974: 🛠️ Refactor suggestion

Add validation for required AI column fields

The bulk insert for AI columns should validate required fields before insertion.

Add validation:

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   const requiredFields = ['fk_model_id', 'fk_column_id', 'model'];
+   const missingFields = requiredFields.filter(field => !column[field]);
+   if (missingFields.length > 0) {
+     throw new Error(`Missing required fields for AI column: ${missingFields.join(', ')}`);
+   }
    insertArr.push({
      fk_model_id: column.fk_model_id,
      fk_column_id: column.id,
      fk_integration_id: column.fk_integration_id,
      model: column.model,
      prompt: column.prompt,
      prompt_raw: column.prompt_raw,
      error: column.error,
    });
  }
  break;
}

Likely invalid or redundant comment.


886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect. It should check for the actual column ID to detect whether the prompt includes the deleted column ID.

Apply this diff to fix the placeholder matching:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nc-gui/components/smartsheet/details/Fields.vue (1)

1040-1043: ⚠️ Potential issue

Undefined function ncDelay

The function ncDelay used in this code is not defined or imported, which will lead to a runtime error. Please ensure that ncDelay is properly defined or consider using an alternative.

Apply this diff to fix the issue by using the delay function from @vueuse/core:

+import { delay } from '@vueuse/core'

 await copy(field.id!)
 isFieldIdCopied.value = true
-await ncDelay(5000)
+await delay(5000)
 isFieldIdCopied.value = false

Likely invalid or redundant comment.

packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (1)

1027-1028: LGTM! Clean implementation of AI column type checking

The isSelectedOnlyAI computed property cleanly handles both AI prompt columns and AI button columns.

packages/nc-gui/components/smartsheet/grid/Table.vue (1)

855-856: LGTM! Improved AI column type checking

The changes improve code maintainability by using dedicated type checking functions isAIPromptCol and isAiButton instead of direct property checks.

packages/nocodb/src/services/columns.service.ts (3)

1601-1645: ⚠️ Potential issue

Add security checks for prompt template processing

The prompt template processing needs additional security checks:

  1. Verify user permissions for referenced columns
  2. Sanitize input to prevent SQL injection
  3. Validate template complexity to prevent DoS attacks
  if (colBody.prompt_raw) {
    await table.getColumns(context);

+   // Validate template complexity
+   const MAX_TEMPLATE_LENGTH = 10000;
+   const MAX_PLACEHOLDERS = 50;
+   if (colBody.prompt_raw.length > MAX_TEMPLATE_LENGTH) {
+     NcError.badRequest(`Template exceeds maximum length of ${MAX_TEMPLATE_LENGTH}`);
+   }
+   if ((colBody.prompt_raw.match(/{/g) || []).length > MAX_PLACEHOLDERS) {
+     NcError.badRequest(`Template exceeds maximum number of placeholders (${MAX_PLACEHOLDERS})`);
+   }

    prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
      const column = table.columns.find((c) => c.title === p1);

      if (!column) {
        NcError.badRequest(`Field '${p1}' not found`);
      }

+     // Verify user has permission to access the column
+     if (!this.hasColumnPermission(context, column, user)) {
+       NcError.forbidden(`No access to field '${p1}'`);
+     }

+     // Sanitize column reference
+     const sanitizedId = this.sanitizeColumnId(column.id);
+     return `{${sanitizedId}}`;
    });
  }

2282-2304: 🛠️ Refactor suggestion

Extract common prompt template processing logic

The prompt template processing logic is duplicated between column update and add operations. Consider extracting it into a shared method.

+ private async processPromptTemplate(
+   context: NcContext,
+   table: Model,
+   promptRaw: string,
+   user: UserType
+ ): Promise<string> {
+   await table.getColumns(context);
+   
+   // Validate template format
+   const MAX_TEMPLATE_LENGTH = 10000;
+   const MAX_PLACEHOLDERS = 50;
+   if (promptRaw.length > MAX_TEMPLATE_LENGTH) {
+     NcError.badRequest(`Template exceeds maximum length of ${MAX_TEMPLATE_LENGTH}`);
+   }
+   if ((promptRaw.match(/{/g) || []).length > MAX_PLACEHOLDERS) {
+     NcError.badRequest(`Template exceeds maximum number of placeholders (${MAX_PLACEHOLDERS})`);
+   }
+
+   return promptRaw.replace(/{(.*?)}/g, (match, p1) => {
+     const column = table.columns.find((c) => c.title === p1);
+     if (!column) {
+       NcError.badRequest(`Field '${p1}' not found`);
+     }
+     if (!this.hasColumnPermission(context, column, user)) {
+       NcError.forbidden(`No access to field '${p1}'`);
+     }
+     return `{${this.sanitizeColumnId(column.id)}}`;
+   });
+ }

  if (isAIPromptCol(colBody)) {
    let prompt = '';
    if (colBody.prompt_raw) {
-     await table.getColumns(context);
-     prompt = colBody.prompt_raw.replace(/{(.*?)}/g, (match, p1) => {
-       const column = table.columns.find((c) => c.title === p1);
-       if (!column) {
-         NcError.badRequest(`Field '${p1}' not found`);
-       }
-       return `{${column.id}}`;
-     });
+     prompt = await this.processPromptTemplate(context, table, colBody.prompt_raw, user);
    }
    colBody.prompt = prompt;
  }

1578-1600: 🛠️ Refactor suggestion

Add transaction handling for AI to non-AI conversion

The conversion from AI to non-AI format should be wrapped in a transaction to maintain data consistency.

  if (
    isAIPromptCol(column) &&
    (colBody.uidt !== UITypes.LongText ||
      (colBody.uidt === UITypes.LongText &&
        colBody.meta?.[LongTextAiMetaProp] !== true))
  ) {
    const baseModel = await reuseOrSave('baseModel', reuse, async () =>
      Model.getBaseModelSQL(context, {
        id: table.id,
        dbDriver: await reuseOrSave('dbDriver', reuse, async () =>
          NcConnectionMgrv2.get(source),
        ),
      }),
    );

+   // Start transaction
+   const trx = await sqlClient.knex.transaction();
+   try {
      await convertAIRecordTypeToValue({
        source,
        table,
        column,
        baseModel,
        sqlClient,
+       trx,
      });
+     await trx.commit();
+   } catch (error) {
+     await trx.rollback();
+     NcError.badRequest(`Failed to convert AI column: ${error.message}`);
+   }
  }

Likely invalid or redundant comment.

@o1lab o1lab force-pushed the nc-feat/ai-prompt branch from 126223b to 62d57b8 Compare December 7, 2024 12:39
@rameshmane7218 rameshmane7218 changed the title Nc feat/ai prompt Nc feat/prompt Dec 7, 2024
Copy link
Contributor
@coderabbitai coderabbitai bot left a 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

🧹 Outside diff range and nitpick comments (13)
packages/nc-gui/utils/commonUtils.ts (1)

103-117: Consider performance optimization for large datasets.

While the implementation is correct and handles edge cases well, consider caching the lowercase query string outside the array iteration for better performance with large arrays.

Here's an optimized version:

 export const searchCompare = (source?: string | number | (string | number | undefined)[], query?: string): boolean => {
+  const normalizedQuery = (query || '').toLowerCase()
   if (ncIsArray(source)) {
     return source.some((item) => {
       return (item || '')
         .toString()
         .toLowerCase()
-        .includes((query || '').toLowerCase())
+        .includes(normalizedQuery)
     })
   }

   return (source || '')
     .toString()
     .toLowerCase()
-    .includes((query || '').toLowerCase())
+    .includes(normalizedQuery)
 }
packages/nc-gui/components/cell/attachment/index.vue (3)

Line range hint 89-116: Refactor the watch handler for better error handling and DRY code

The current implementation has duplicated logic for resetting arrays. Consider refactoring to reduce duplication and improve error handling.

 watch(
   () => modelValue,
   async (nextModel) => {
+    const resetArrays = () => {
+      if (isPublic.value && isForm.value) {
+        storedFiles.value = []
+      } else {
+        attachments.value = []
+      }
+    }
+
     if (nextModel) {
       try {
         const nextAttachments = ((typeof nextModel === 'string' ? JSON.parse(nextModel) : nextModel) || []).filter(Boolean)
-
         if (isPublic.value && isForm.value) {
           storedFiles.value = nextAttachments
         } else {
           attachments.value = nextAttachments
         }
       } catch (e) {
-        console.error(e)
+        console.error('Failed to parse attachment data:', e)
-        if (isPublic.value && isForm.value) {
-          storedFiles.value = []
-        } else {
-          attachments.value = []
-        }
+        resetArrays()
       }
     } else {
-      if (isPublic.value && isForm.value) {
-        storedFiles.value = []
-      } else {
-        attachments.value = []
-      }
+      resetArrays()
     }
   },
   {
     immediate: true,
   },
 )

Line range hint 186-201: Enhance type safety and event handling in keyboard interactions

The keyboard event handlers could benefit from more specific TypeScript types and improved event handling.

-const keydownEnter = (e: KeyboardEvent) => {
+const keydownEnter = (e: KeyboardEvent & { currentTarget: HTMLElement }) => {
   if (!isSurveyForm.value) {
     open(e)
     e.stopPropagation()
+    e.preventDefault()  // Prevent form submission
   }
 }

-const keydownSpace = (e: KeyboardEvent) => {
+const keydownSpace = (e: KeyboardEvent & { currentTarget: HTMLElement }) => {
   if (isSurveyForm.value) {
     open(e)
     e.stopPropagation()
+    e.preventDefault()  // Prevent page scroll
   }
 }

427-427: Enhance accessibility for the maximize button

While replacing the expand icon with maximize icon, consider adding ARIA attributes for better accessibility.

-          <component :is="iconMap.maximize" v-else class="transform group-hover:(!text-grey-800) text-gray-700 w-3 h-3" />
+          <component 
+            :is="iconMap.maximize" 
+            v-else 
+            class="transform group-hover:(!text-grey-800) text-gray-700 w-3 h-3"
+            role="img"
+            aria-label="Maximize attachment view"
+          />
packages/nc-gui/utils/columnUtils.ts (2)

27-32: Consider adding JSDoc documentation for the new AIPrompt type

While the implementation is correct, adding documentation would help other developers understand the purpose and usage of this new UI type.

+/**
+ * AIPrompt type for AI-assisted text generation
+ * @property {string} name - The type identifier
+ * @property {FunctionalComponent} icon - The icon component
+ * @property {number} isNew - Flag indicating this is a new feature
+ * @property {number} deprecated - Flag indicating deprecation status
+ */
  {
    name: AIPrompt,
    icon: iconMap.cellAi,
    isNew: 1,
    deprecated: 0,
  },

269-277: Consider making the tooltip message more specific

The default tooltip message 'msg.invalidColumnConfiguration' is generic. Consider making it more descriptive or adding context about what aspect of the configuration is invalid.

  const result = {
    isInvalid: false,
-   tooltip: 'msg.invalidColumnConfiguration',
+   tooltip: 'msg.pleaseConfigureColumn',
  }
packages/nc-gui/composables/useIntegrationsStore.ts (1)

Line range hint 447-496: Add null checks and improve type safety

The function handles dynamic integrations loading well, but there are a few improvements that could be made:

  1. Use optional chaining for response?.id check
  2. Add type safety for the iconMap access

Apply these improvements:

- if (response && response?.id) {
+ if (response?.id) {

- if (di.meta.icon in iconMap) {
+ if (di.meta.icon && di.meta.icon in iconMap) {

Also, consider adding error handling for the API call:

+ try {
    const dynamicIntegrations = (await $api.integrations.list()) as {
      type: IntegrationsType
      subType: string
      meta: {
        title?: string
        icon?: string
        description?: string
        order?: number
      }
    }[]
+  } catch (e) {
+    console.error('Failed to load dynamic integrations:', e)
+    return
+  }
packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (3)

Line range hint 279-297: Document the ESLint directive and new prop

The code functionality looks correct, but could benefit from additional documentation:

  1. Add a comment explaining why the ESLint rule needs to be disabled
  2. Document the call-add-empty-row prop's purpose and usage

Line range hint 279-324: Consider caching the expanded row index

The getExpandedRowIndex() is called multiple times in computed properties and navigation methods. Consider caching this value when the route changes to improve performance.

+ const expandedRowIndex = ref(-1)
+ 
+ watch(() => routeQuery.value.rowId, () => {
+   expandedRowIndex.value = formattedData.value.findIndex(
+     (row: Row) => routeQuery.value.rowId === extractPkFromRow(row.row, meta.value?.columns as ColumnType[])
+   )
+ })
+ 
  const isLastRow = computed(() => {
-   const currentIndex = getExpandedRowIndex()
+   const currentIndex = expandedRowIndex.value
    return vGroup.value.paginationData?.isLastPage && currentIndex === formattedData.value.length - 1
  })

Line range hint 279-324: Enhance error handling in deleteSelectedRowsWrapper

The function should handle potential errors during deletion and data reload operations.

 async function deleteSelectedRowsWrapper() {
   if (!deleteSelectedRows) return
 
+  try {
     await deleteSelectedRows()
     // reload table data
     await reloadTableData({ shouldShowLoading: false })
+  } catch (error) {
+    console.error('Failed to delete selected rows:', error)
+    message.error(t('msg.error.deleteRecord'))
+  }
 }
packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (1)

Line range hint 174-212: LGTM! Consider localizing error messages.

The validation rules and default values for AI button type are well-structured. However, the error message "At least one output field is required for AI Button" should use the i18n translation system for consistency.

- message: 'At least one output field is required for AI Button',
+ message: t('msg.error.aiButtonOutputFieldRequired'),
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (1)

Line range hint 263-285: LGTM! Consider enhancing type safety.

The onSelectType function properly handles AI prompt type selection. However, consider using a type union for better type safety.

- const  UITypes | typeof AIButton | typeof AIPrompt, fromSearchList = false)
+ type SelectableType = UITypes | typeof AIButton | typeof AIPrompt
+ const  SelectableType, fromSearchList = false)
packages/nocodb/src/db/BaseModelSqlv2.ts (1)

5721-5724: Improve error handling in Promise.all

The Promise.all usage for raw data preparation is good for parallel processing, but consider adding error handling for individual promise rejections.

- insertDatas.map(
-   async (d) =>
-     await this.prepareNocoData(d, true, cookie, null, { raw }),
- ),
+ insertDatas.map(async (d) => {
+   try {
+     return await this.prepareNocoData(d, true, cookie, null, { raw });
+   } catch (e) {
+     logger.error(`Error preparing data: ${e.message}`);
+     throw e;
+   }
+ }),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 126223b and 62d57b8.

📒 Files selected for processing (70)
  • packages/nc-gui/components/ai/PromptWithFields.vue (8 hunks)
  • packages/nc-gui/components/ai/Settings.vue (9 hunks)
  • packages/nc-gui/components/cell/AI.vue (8 hunks)
  • packages/nc-gui/components/cell/TextArea.vue (11 hunks)
  • packages/nc-gui/components/cell/attachment/index.vue (1 hunks)
  • packages/nc-gui/components/general/IntegrationIcon.vue (1 hunks)
  • packages/nc-gui/components/general/Loader.vue (1 hunks)
  • packages/nc-gui/components/nc/Button.vue (6 hunks)
  • packages/nc-gui/components/nc/List/RecordItem.vue (1 hunks)
  • packages/nc-gui/components/nc/Switch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/Cell.vue (3 hunks)
  • packages/nc-gui/components/smartsheet/Gallery.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/Kanban.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/PlainCell.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue (6 hunks)
  • packages/nc-gui/components/smartsheet/column/ButtonOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (8 hunks)
  • packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue (2 hunks)
  • packages/nc-gui/components/smartsheet/details/Fields.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/grid/Table.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/Cell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts (1 hunks)
  • packages/nc-gui/components/smartsheet/header/Menu.vue (4 hunks)
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue (1 hunks)
  • packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/Button.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/HasMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue (1 hunks)
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue (0 hunks)
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue (2 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue (1 hunks)
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue (2 hunks)
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue (2 hunks)
  • packages/nc-gui/composables/useColumnCreateStore.ts (2 hunks)
  • packages/nc-gui/composables/useData.ts (2 hunks)
  • packages/nc-gui/composables/useInfiniteData.ts (2 hunks)
  • packages/nc-gui/composables/useIntegrationsStore.ts (8 hunks)
  • packages/nc-gui/composables/useMultiSelect/index.ts (2 hunks)
  • packages/nc-gui/composables/useNocoAi.ts (3 hunks)
  • packages/nc-gui/helpers/parsers/parserHelpers.ts (2 hunks)
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue (3 hunks)
  • packages/nc-gui/utils/cell.ts (1 hunks)
  • packages/nc-gui/utils/columnUtils.ts (4 hunks)
  • packages/nc-gui/utils/commonUtils.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/UITypes.ts (3 hunks)
  • packages/nocodb-sdk/src/lib/globals.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/helperFunctions.ts (1 hunks)
  • packages/nocodb-sdk/src/lib/index.ts (1 hunks)
  • packages/nocodb/src/db/BaseModelSqlv2.ts (8 hunks)
  • packages/nocodb/src/db/conditionV2.ts (2 hunks)
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts (2 hunks)
  • packages/nocodb/src/db/sortV2.ts (2 hunks)
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts (3 hunks)
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts (1 hunks)
  • packages/nocodb/src/models/AIColumn.ts (1 hunks)
  • packages/nocodb/src/models/Column.ts (9 hunks)
  • packages/nocodb/src/models/Integration.ts (3 hunks)
  • packages/nocodb/src/models/LongTextColumn.ts (1 hunks)
  • packages/nocodb/src/models/index.ts (1 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts (2 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts (4 hunks)
  • packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (5 hunks)
  • packages/nocodb/src/services/columns.service.ts (4 hunks)
  • packages/nocodb/src/utils/dataConversion.ts (1 hunks)
  • packages/nocodb/src/utils/globals.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/nc-gui/components/workspace/integrations/ConnectionsTab.vue
🚧 Files skipped from review as they are similar to previous changes (42)
  • packages/nocodb-sdk/src/lib/globals.ts
  • packages/nocodb-sdk/src/lib/index.ts
  • packages/nocodb/src/models/index.ts
  • packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  • packages/nocodb/src/meta/migrations/v2/nc_069_ai_prompt.ts
  • packages/nc-gui/components/general/IntegrationIcon.vue
  • packages/nc-gui/components/smartsheet/column/UITypesOptionsWithSearch.vue
  • packages/nc-gui/helpers/tiptapExtensions/mention/FieldList.vue
  • packages/nc-gui/components/smartsheet/header/Cell.vue
  • packages/nc-gui/components/virtual-cell/HasMany.vue
  • packages/nc-gui/components/virtual-cell/Button.vue
  • packages/nc-gui/components/workspace/project/AiCreateProject.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddCommon.vue
  • packages/nc-gui/components/nc/Switch.vue
  • packages/nc-gui/components/smartsheet/Gallery.vue
  • packages/nc-gui/components/nc/Button.vue
  • packages/nc-gui/helpers/parsers/parserHelpers.ts
  • packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts
  • packages/nc-gui/components/smartsheet/header/CellIcon.ts
  • packages/nc-gui/components/virtual-cell/ManyToMany.vue
  • packages/nc-gui/components/smartsheet/PlainCell.vue
  • packages/nc-gui/components/workspace/integrations/forms/EditOrAddDatabase.vue
  • packages/nc-gui/components/virtual-cell/components/ListItem.vue
  • packages/nc-gui/composables/useMultiSelect/index.ts
  • packages/nocodb/src/db/sortV2.ts
  • packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  • packages/nc-gui/composables/useInfiniteData.ts
  • packages/nc-gui/utils/cell.ts
  • packages/nc-gui/components/ai/Settings.vue
  • packages/nc-gui/components/workspace/integrations/IntegrationsTab.vue
  • packages/nocodb/src/utils/globals.ts
  • packages/nc-gui/components/smartsheet/column/AiButtonOptions.vue
  • packages/nc-gui/composables/useColumnCreateStore.ts
  • packages/nc-gui/components/smartsheet/expanded-form/index.vue
  • packages/nc-gui/components/smartsheet/Cell.vue
  • packages/nc-gui/components/general/Loader.vue
  • packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  • packages/nc-gui/composables/useData.ts
  • packages/nc-gui/components/nc/List/RecordItem.vue
  • packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  • packages/nc-gui/components/smartsheet/details/Fields.vue
  • packages/nc-gui/components/smartsheet/Kanban.vue
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  • packages/nc-gui/components/smartsheet/grid/Table.vue
  • packages/nocodb/src/services/columns.service.ts
🧰 Additional context used
📓 Learnings (1)
packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (1)
Learnt from: DarkPhoenix2704
PR: nocodb/nocodb#8781
File: packages/nc-gui/components/smartsheet/grid/GroupBy.vue:225-232
Timestamp: 2024-11-12T03:23:04.401Z
Learning: DarkPhoenix2704 prefers to keep code implementations that prioritize readability, even if an alternative might be more concise.
🪛 Biome (1.9.4)
packages/nc-gui/composables/useIntegrationsStore.ts

[error] 296-296: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/Column.ts

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/models/AIColumn.ts

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

🔇 Additional comments (49)
packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue (1)

155-158: ⚠️ Potential issue

Fix missing import for ncDelay function

The ncDelay function is called but not imported, which will cause a runtime error. This issue was previously identified but remains unfixed.

Import the ncDelay utility:

<script lang="ts" setup>
import { ProjectRoles, type TableType, type ViewType, WorkspaceUserRoles } from 'nocodb-sdk'
import { ViewTypes, viewTypeAlias } from 'nocodb-sdk'
import { LockType } from '#imports'
+ import { ncDelay } from '~/utils/ncDelay'
packages/nc-gui/utils/commonUtils.ts (2)

75-102: Well-documented with comprehensive examples!

The JSDoc documentation is excellent, providing clear parameter descriptions and covering various use cases with examples.


103-117: Add test coverage for the searchCompare function.

As mentioned in a previous review, this utility function needs test coverage. Based on the documentation and implementation, consider adding tests for:

  1. Basic string/number searches
  2. Array searches with mixed types
  3. Edge cases (undefined, null, empty values)
  4. Case sensitivity scenarios

Let's verify existing test coverage:

#!/bin/bash
# Description: Check for existing test coverage of searchCompare function
# Look for test files and any usage of searchCompare

# Search for test files
fd -e spec.ts -e test.ts commonUtils packages/nc-gui/

# Search for usage of searchCompare
rg "searchCompare" packages/nc-gui/ -l
packages/nc-gui/components/cell/attachment/index.vue (1)

Line range hint 1-585: Overall implementation looks good!

The changes align well with the PR objectives. The code implements all the described functionality including:

  • Enhanced attachment modal handling
  • Improved keyboard interactions
  • Updated UI elements
  • Proper error handling for attachment data

The implementation is consistent with similar changes in other components mentioned in the AI summary.

packages/nc-gui/utils/columnUtils.ts (2)

2-3: LGTM: Clean addition of AI-related types and constants

The new imports and constants are well-organized and follow the existing pattern.

Also applies to: 15-17


289-295: 🛠️ Refactor suggestion

DRY up duplicate AI validation logic

The AI integration validation logic is duplicated between Button and LongText types. This violates the DRY principle and makes maintenance harder.

Extract the common logic into a helper function:

+ const isAiColumnInvalid = (
+   colOptions: ButtonType,
+   isReadOnly: boolean,
+   aiIntegrations: Partial<IntegrationType>[]
+ ): { isInvalid: boolean; tooltip: string } => ({
+   isInvalid:
+     !colOptions.fk_integration_id ||
+     (isReadOnly
+       ? false
+       : !!colOptions.fk_integration_id && 
+         !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id')),
+   tooltip: 'title.aiIntegrationMissing'
+ })

  case UITypes.Button: {
    const colOptions = col.colOptions as ButtonType
    if (colOptions.type === ButtonActionsType.Webhook) {
      result.isInvalid = !colOptions.fk_webhook_id
    } else if (colOptions.type === ButtonActionsType.Url) {
      result.isInvalid = !!colOptions.error
    } else if (colOptions.type === ButtonActionsType.Ai) {
-     result.isInvalid =
-       !colOptions.fk_integration_id ||
-       (isReadOnly
-         ? false
-         : !!colOptions.fk_integration_id && 
-           !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-     result.tooltip = 'title.aiIntegrationMissing'
+     const aiValidation = isAiColumnInvalid(colOptions, isReadOnly, aiIntegrations)
+     result.isInvalid = aiValidation.isInvalid
+     result.tooltip = aiValidation.tooltip
    }
    break
  }
  case UITypes.LongText: {
    if (parseProp(col.meta)[LongTextAiMetaProp]) {
      const colOptions = col.colOptions as ButtonType
-     result.isInvalid =
-       !colOptions.fk_integration_id ||
-       (isReadOnly
-         ? false
-         : !!colOptions.fk_integration_id && 
-           !ncIsArrayIncludes(aiIntegrations, colOptions.fk_integration_id, 'id'))
-     result.tooltip = 'title.aiIntegrationMissing'
+     const aiValidation = isAiColumnInvalid(colOptions, isReadOnly, aiIntegrations)
+     result.isInvalid = aiValidation.isInvalid
+     result.tooltip = aiValidation.tooltip
    }
    break
  }

Also applies to: 303-309

packages/nc-gui/composables/useIntegrationsStore.ts (5)

191-192: LGTM: Event tracking implementation is consistent and well-placed

The event tracking calls are strategically placed before each operation and follow a consistent naming pattern.

Also applies to: 224-225, 253-254, 287-292


541-541: LGTM: Export addition is consistent with the existing pattern


93-94: 🛠️ Refactor suggestion

Consider using a computed property for AI integrations

The manual synchronization of AI integrations state across multiple functions could lead to race conditions.

Consider using a computed property as suggested in the previous review to derive AI integrations from the main integrations array:

- const { aiIntegrations } = useNocoAi()
+ const aiIntegrations = computed(() => 
+   integrations.value.filter(i => i.type === IntegrationsType.Ai)
+   .map(i => ({
+     id: i.id,
+     title: i.title,
+     is_default: i.is_default,
+     type: i.type,
+     sub_type: i.sub_type,
+   }))
+ )

229-238: 🛠️ Refactor suggestion

Avoid mutating objects within map function

The current implementation mutates the object directly within the map function.

Apply this diff to return new objects instead:

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.title = integration.title
-     return i
+     return { ...i, title: integration.title }
    }
    return i
  })
}

258-269: 🛠️ Refactor suggestion

Avoid mutating objects within map function

Similar mutation pattern in setDefaultIntegration function.

Apply this diff to return new objects instead:

if (integration.type === IntegrationsType.Ai) {
  aiIntegrations.value = aiIntegrations.value.map((i) => {
    if (i.id === integration.id) {
-     i.is_default = true
+     return { ...i, is_default: true }
    } else {
-     i.is_default = false
+     return { ...i, is_default: false }
    }
  })
}
packages/nc-gui/components/cell/AI.vue (4)

16-18: LGTM! Improved permission handling.

The addition of isUIAllowed and isPublic enhances access control by properly managing user permissions and public access scenarios.


118-120: LGTM! Well-structured button state management.

The isDisabledAiButton computed property effectively consolidates multiple conditions into a single, readable expression for managing the AI button's disabled state.


131-139: Fix the tooltip disabled logic.

The tooltip's disabled condition is still incorrect. It should be disabled when the button is enabled, not the other way around.

Apply this diff to fix the issue:

-    <NcTooltip :disabled="isFieldAiIntegrationAvailable || isPublic || isUIAllowed('dataEdit')" class="flex">
+    <NcTooltip :disabled="!isDisabledAiButton" class="flex">

Line range hint 194-211: LGTM! Enhanced visual feedback.

The styling changes improve user experience by providing clear visual feedback for different button states, particularly in read-only mode.

packages/nc-gui/components/ai/PromptWithFields.vue (3)

Line range hint 19-31: LGTM! Enhanced component flexibility.

The addition of the readOnly prop improves component reusability by allowing external control of the edit state.


159-187: LGTM! Proper tooltip lifecycle management.

The implementation correctly manages tooltip instances and includes cleanup on component unmount to prevent memory leaks.


63-68: Correct the string comparison logic in the suggestion filter.

The string comparison still has an extra closing brace that may lead to incorrect filtering.

Apply this diff to fix the issue:

return (
  props.options?.filter(
    (o) =>
-     o.title?.toLowerCase()?.includes(query.toLowerCase()) || `${o.title?.toLowerCase()}}` === query.toLowerCase(),
+     o.title?.toLowerCase()?.includes(query.toLowerCase()) || `{${o.title?.toLowerCase()}}` === query.toLowerCase(),
  ) ?? []
)
packages/nc-gui/components/cell/TextArea.vue (4)

Line range hint 17-41: Consider using a default value for meta injection.

The injected meta dependency uses ref() as a fallback, which could lead to runtime errors when accessing properties.

Apply this diff to add a proper default value:

-const meta = inject(MetaInj, ref())
+const meta = inject(MetaInj, ref({ columns: [] }))

100-111: Avoid using non-null assertions to prevent potential runtime errors.

The use of non-null assertions (!) in meta.value!.columns! may lead to runtime errors if meta.value or columns are undefined.

Apply this diff to handle possible null values:

const rowId = computed(() => {
-  return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
+  if (currentRow.value?.row && meta.value?.columns) {
+    return extractPkFromRow(currentRow.value.row, meta.value.columns)
+  }
+  return null
})

Line range hint 426-561: LGTM! Well-structured AI feature UI.

The UI changes provide clear feedback for AI-related features with proper handling of:

  • Warning states for stale data
  • Loading indicators
  • User editing states
  • Regeneration controls

647-648: Correct the usage of Tailwind CSS classes.

The class animate-infinite is not a valid Tailwind CSS utility. The animate-spin class already provides an infinite spin animation.

Apply this diff to correct the class:

<GeneralIcon icon="refresh" :class="{
-  'animate-infinite animate-spin': isAiGenerating
+  'animate-spin': isAiGenerating
}" />
packages/nc-gui/components/smartsheet/grid/GroupByTable.vue (1)

Line range hint 279-324: Verify pagination handling for edge cases

Please ensure proper handling of the following scenarios:

  1. Navigation when deleting the last row of a page
  2. Navigation when adding a row that creates a new page
✅ Verification successful

Based on the shell script results, I can now provide a definitive response about the pagination handling in the GroupByTable component:

Pagination edge cases are properly handled in the codebase

The code shows robust pagination handling for the mentioned scenarios:

  1. When deleting the last row of a page:

    • The component syncs with paginationData and automatically adjusts page numbers
    • If the current page becomes empty, it navigates to the previous page via loadGroupPage
  2. When adding a row that creates a new page:

    • The pagination state is properly managed through vGroup.paginationData
    • Page navigation is handled through the loadGroupPage callback
    • The component maintains proper page size limits and total row counts

The implementation includes proper page boundary checks (isFirstPage, isLastPage), page size management, and navigation controls, making it resilient to edge cases in row manipulation scenarios.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for pagination-related tests and edge case handling
rg -A 5 "loadGroupPage|paginationData" --type vue

Length of output: 79


Script:

#!/bin/bash
# Search for pagination-related code with ripgrep, without file type restriction
rg -A 5 "loadGroupPage|paginationData"

Length of output: 75492


Script:

#!/bin/bash
# Search for pagination-related tests
fd "test|spec" --exec rg -l "loadGroupPage|paginationData" {} \;

Length of output: 69


Script:

#!/bin/bash
# Look for pagination handling in the component
ast-grep --pattern 'async function loadGroupPage($_, $_) {
  $$$
}'

Length of output: 69


Script:

#!/bin/bash
# Search for group table pagination implementation
rg -A 10 "class=\"nc-group-table\""

Length of output: 1129

packages/nocodb/src/models/AIColumn.ts (3)

14-16: ⚠️ Potential issue

Ensure correct handling of 'null' or 'undefined' in castType method

The castType method should handle cases where data might be null or undefined. Currently, if data is falsy, the method returns data, which could be undefined instead of an AIColumn instance.

Apply this diff to fix the issue:

-      return data && new AIColumn(data);
+      return data ? new AIColumn(data) : null;

26-26: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. It's better to reference the class name directly for readability.

Apply this diff to fix the issue:

-        return this._insert(
+        return AIColumn._insert(
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


40-40: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. It's better to reference the class name directly for readability.

Apply this diff to fix the issue:

-        return this._update(
+        return AIColumn._update(
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/models/LongTextColumn.ts (2)

61-61: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. It's better to reference the class name directly for readability.

Apply this diff to fix the issue:

-        return this.read(context, longTextColumn.fk_column_id, ncMeta);
+        return LongTextColumn.read(context, longTextColumn.fk_column_id, ncMeta);
🧰 Tools
🪛 Biome (1.9.4)

[error] 61-61: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


85-85: 🛠️ Refactor suggestion

Avoid using 'this' in static methods; use the class name instead

Using this in static methods can be unclear. It's better to reference the class name directly for readability.

Apply this diff to fix the issue:

-        return column ? this.castType(column) : null;
+        return column ? LongTextColumn.castType(column) : null;
🧰 Tools
🪛 Biome (1.9.4)

[error] 85-85: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

packages/nocodb/src/utils/dataConversion.ts (1)

135-149: ⚠️ Potential issue

Avoid string concatenation in SQL queries to prevent SQL injection

In the MSSQL query, string concatenation is used to build the JSON string, which can lead to SQL injection vulnerabilities. It's important to construct SQL queries using parameterized statements.

Consider refactoring the query to use parameter bindings or MSSQL's JSON functions without concatenation. For example, you can use JSON_MODIFY or pass the entire JSON object as a parameter.

Apply this diff to fix the issue:

-          `UPDATE ??
-            SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}')
-            WHERE ?? is not null;`,
+          `UPDATE ??
+            SET ?? = JSON_QUERY(?)
+            WHERE ?? is not null;`,

          [
            baseModel.getTnPath(table.table_name),
            column.column_name,
-           column.column_name,
-           commonRecord.lastModifiedBy.toString(),
-           commonRecord.lastModifiedTime.toString(),
-           commonRecord.isStale,
+           JSON.stringify({
+             value: sqlClient.raw('??', [column.column_name]),
+             lastModifiedBy: commonRecord.lastModifiedBy.toString(),
+             lastModifiedTime: commonRecord.lastModifiedTime.toString(),
+             isStale: commonRecord.isStale,
+           }),
            column.column_name,
          ],
packages/nc-gui/composables/useNocoAi.ts (1)

20-28: Code changes look good

The updates to the AI integration handling improve the logic and fix previous issues related to checking the availability of AI integrations.

packages/nc-gui/components/smartsheet/column/LongTextOptions.vue (2)

48-58: Initialize meta object before assignment

The isEnabledGenerateText setter attempts to modify meta without checking if it exists.


66-104: Add error handling for generateRows

The generate function lacks error handling for the asynchronous generateRows call.

packages/nocodb/src/models/Integration.ts (2)

454-457: LGTM: Safe initialization of config.models array

The check and initialization of config.models as an empty array is a good defensive programming practice.


474-500: Refactor duplicate unbinding logic

The unbinding logic is duplicated between delete and softDelete methods.

Also applies to: 522-547

packages/nc-gui/components/smartsheet/header/Menu.vue (2)

48-49: LGTM!

Clean integration of the AI functionality through the useNocoAi hook.


52-61: LGTM! Well-structured validation logic.

The columnInvalid computed property effectively handles validation for AI-related columns with proper null checks and a clean return type.

packages/nc-gui/components/smartsheet/column/EditOrAdd.vue (3)

186-196: LGTM! Clean implementation of AI prompt visibility control.

The hideAiPromptOnEdit condition effectively prevents editing of AI prompts and buttons in edit mode, maintaining a consistent user experience.


625-628: LGTM! Clear and focused implementation.

The isAiPromptSelectOption function provides a clean way to identify AI prompt fields.


1165-1169: LGTM! Good integration of AI functionality.

The SmartsheetColumnLongTextOptions component is properly enhanced with AI integration navigation capabilities.

packages/nocodb/src/db/conditionV2.ts (1)

596-613: 🛠️ Refactor suggestion

Add error handling for JSON extraction

The JSON extraction logic should handle invalid JSON values to prevent runtime errors.

Apply this diff to add error handling:

if (isAIPromptCol(column)) {
+ const handleInvalidJson = (field) => {
+   return knex.raw('CASE WHEN JSON_VALID(??) THEN ? ELSE NULL END', [
+     column.column_name,
+     field
+   ]);
+ };

  if (knex.clientType() === 'pg') {
-   field = knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`TRIM('"' FROM (??::jsonb->>'value'))`, [column.column_name])
+   );
  } else if (knex.clientType().startsWith('mysql')) {
-   field = knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name]);
+   field = handleInvalidJson(
+     knex.raw(`JSON_UNQUOTE(JSON_EXTRACT(??, '$.value'))`, [column.column_name])
+   );
  } // Apply similar changes for other DB types
}
packages/nocodb/src/models/Column.ts (4)

573-577: LGTM!

The implementation correctly retrieves AI column options.


1101-1106: LGTM!

The case statement for handling AI column type is correctly implemented.


452-469: 🛠️ Refactor suggestion

Add error handling for AI column insertion

The AI column insertion lacks error handling. Consider adding try-catch and logging.

Apply this diff:

case UITypes.LongText: {
  if (column.meta?.[LongTextAiMetaProp] === true) {
+   try {
      await AIColumn.insert(
        context,
        {
          fk_model_id: column.fk_model_id,
          fk_column_id: colId,
          fk_integration_id: column.fk_integration_id,
          model: column.model,
          prompt: column.prompt,
          prompt_raw: column.prompt_raw,
          error: column.error,
        },
        ncMeta,
      );
+   } catch (error) {
+     logger.error(`Failed to insert AI column: ${error.message}`);
+     throw error;
+   }
  }
  break;
}

886-927: ⚠️ Potential issue

Fix incorrect placeholder matching in AI prompts

The code checks for a hardcoded {column_id} placeholder in AI prompts, which is incorrect. It should check for the actual column ID.

Apply this diff:

- if (ai.prompt && ai.prompt.match(/{column_id}/)) {
+ if (ai.prompt && ai.prompt.includes(`{${col.id}}`)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 921-921: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts (2)

1113-1114: LGTM!

The AI prompt column check is correctly added to the filter condition.


1281-1296: 🛠️ Refactor suggestion

Add validation for required fields in AI prompt column import

The AI prompt column import lacks validation for required fields.

Apply this diff:

} else if (isAIPromptCol(col)) {
  if (base.fk_workspace_id !== colOptions.fk_workspace_id) {
    colOptions.fk_workspace_id = null;
    colOptions.fk_integration_id = null;
    colOptions.model = null;
  }

+ // Validate required fields
+ if (!colOptions.prompt_raw) {
+   this.logger.warn(`Skipping AI prompt column import: Missing prompt_raw for column ${col.title}`);
+   continue;
+ }

+ try {
    const freshModelData = await this.columnsService.columnAdd(context, {
      tableId: getIdOrExternalId(getParentIdentifier(col.id)),
      column: withoutId({
        ...flatCol,
        ...{
          fk_integration_id: colOptions.fk_integration_id,
          model: colOptions.model,
          prompt_raw: colOptions.prompt_raw,
        },
      }) as any,
      req: param.req,
      user: param.user,
    });
+ } catch (error) {
+   this.logger.error(`Failed to import AI prompt column ${col.title}: ${error.message}`);
+   throw error;
+ }
packages/nocodb/src/db/BaseModelSqlv2.ts (3)

10063-10105: LGTM! Well-structured AI prompt data handling

The implementation properly handles AI prompt column data with metadata tracking for:

  • Value changes
  • Last modified info
  • Staleness state
  • System vs user modifications

10108-10138: LGTM! Robust AI column staleness tracking

The implementation correctly:

  • Tracks referenced column changes
  • Updates staleness state
  • Preserves existing metadata

10071-10076: ⚠️ Potential issue

Correct the type of isStale to boolean

The isStale property is used as a boolean in the code but typed as string in the interface.

const obj: {
  value?: string;
  lastModifiedBy?: string;
  lastModifiedTime?: string;
- isStale?: string;
+ isStale?: boolean;
} = {};

Likely invalid or redundant comment.

@mertmit mertmit closed this Dec 7, 2024
@dosubot dosubot bot removed the size:XL This PR changes 500-999 lines, ignoring generated files. label Dec 7, 2024
@dosubot dosubot bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Dec 7, 2024
@mertmit mertmit deleted the nc-feat/ai-prompt branch December 7, 2024 12:51
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
@nocodb nocodb deleted a comment from coderabbitai bot Dec 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size:XS This PR changes 0-9 lines, ignoring generated files. 🔦 Type: Feature New functionality is requested.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0