@ -1,13 +1,147 @@
<!-- File not in use for now -- >
< script setup lang = "ts" >
import { isAIPromptCol , UITypes } from 'nocodb-sdk'
const props = defineProps < {
value : any
modelV alue: any
} > ( )
const emit = defineEmits ( [ 'update:value' ] )
const emit = defineEmits ( [ 'update:modelValue' , 'navigateToIntegrations' ] )
const { t } = useI18n ( )
const meta = inject ( MetaInj ) !
const workspaceStore = useWorkspace ( )
const { activeWorkspaceId } = storeToRefs ( workspaceStore )
const availableFields = computed ( ( ) => {
if ( ! meta . value ? . columns ) return [ ]
return meta . value . columns . filter ( ( c ) => c . title && ! c . system && c . uidt !== UITypes . ID )
} )
const vModel = useVModel ( props , 'modelValue' , emit )
const { isEdit , setAdditionalValidations , column , formattedData , loadData , disableSubmitBtn } = useColumnCreateStoreOrThrow ( )
const { aiIntegrationAvailable , generateRows } = useNocoAi ( )
const { isFeatureEnabled } = useBetaFeatureToggle ( )
const previewRow = ref < Row > ( {
row : { } ,
oldRow : { } ,
rowMeta : { new : true } ,
} )
const previewFieldTitle = ref ( vModel . value . title || 'temp_title' )
const generatingPreview = ref ( false )
const isAlreadyGenerated = ref ( false )
const isPreviewEnabled = computed ( ( ) => {
const isFieldAddedInPromt = availableFields . value . some ( ( f ) => {
return vModel . value . prompt _raw ? . includes ( ` { ${ f . title } } ` )
} )
return isFieldAddedInPromt
} )
const isEnabledGenerateText = computed ( {
get : ( ) => {
return vModel . value . meta ? . [ LongTextAiMetaProp ]
} ,
set : ( value : boolean ) => {
vModel . value . meta [ LongTextAiMetaProp ] = value
vModel . value . prompt _raw = ''
previewRow . value . row = { }
isAlreadyGenerated . value = false
} ,
} )
const loadViewData = async ( ) => {
if ( ! formattedData . value . length ) {
await loadData ( undefined , false )
}
}
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'
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 ] ,
)
const vModel = useVModel ( props , 'value' , emit )
if ( res ? . length && res [ 0 ] ? . [ previewFieldTitle . value ] ) {
previewRow . value . row = {
... res [ 0 ] ,
[ previewFieldTitle . value ] : {
value : res [ 0 ] ? . [ previewFieldTitle . value ] ,
} ,
}
isAlreadyGenerated . value = true
}
generatingPreview . value = false
}
const isPromptEnabled = computed ( ( ) => {
if ( isEdit . value ) {
return isAIPromptCol ( column . value ) || isFeatureEnabled ( FEATURE _FLAG . AI _FEATURES )
}
return isFeatureEnabled ( FEATURE _FLAG . AI _FEATURES )
} )
onMounted ( ( ) => {
/ / s e t d e f a u l t v a l u e
vModel . value . prompt _raw = ( column ? . value ? . colOptions as Record < string , any > ) ? . prompt _raw || ''
} )
const validators = {
fk _integration _id : [
{
validator : ( _ : any , value : any ) => {
return new Promise < void > ( ( resolve , reject ) => {
if ( isEnabledGenerateText . value && ! value ) {
reject ( new Error ( t ( 'title.aiIntegrationMissing' ) ) )
}
resolve ( )
} )
} ,
} ,
] ,
}
if ( isEdit . value ) {
vModel . value . fk _integration _id = vModel . value ? . colOptions ? . fk _integration _id
}
setAdditionalValidations ( {
... validators ,
} )
provide ( EditColumnInj , ref ( true ) )
const richMode = computed ( {
get : ( ) => ! ! vModel . value . meta ? . richMode ,
@ -18,21 +152,214 @@ const richMode = computed({
} ,
} )
const handleDisableSubmitBtn = ( ) => {
if ( ! isEnabledGenerateText . value ) {
if ( disableSubmitBtn . value ) {
disableSubmitBtn . value = false
}
return
}
if ( isPreviewEnabled . value ) {
disableSubmitBtn . value = false
} else {
disableSubmitBtn . value = true
}
}
watch ( richMode , ( ) => {
vModel . value . cdf = null
} )
watch ( isPreviewEnabled , handleDisableSubmitBtn , {
immediate : true ,
} )
< / script >
< template >
< div class = "flex flex-col gap-2" >
< div class = "flex flex-col gap-4 " >
< a -form -item >
< div class = "flex items-center gap-1" >
< NcSwitch v -model :checked ="richMode" >
< div class = "text-sm text-gray-800 select-none" >
{ { $t ( 'labels.enableRichText' ) } }
< NcTooltip :disabled ="!isEnabledGenerateText" >
< template # title > Rich text formatting is not supported when generate text using AI is enabled < / template >
< div class = "flex items-center gap-1" >
< NcSwitch v -model :checked ="richMode" :disabled ="isEnabledGenerateText" >
< div class = "text-sm text-gray-800 select-none font-semibold" >
{ { $t ( 'labels.enableRichText' ) } }
< / div >
< / NcSwitch >
< / div >
< / NcTooltip >
< / a - f o r m - i t e m >
< div v-if ="isPromptEnabled" class="relative" >
< a -form -item class = "flex items-center" >
< NcTooltip :disabled ="!richMode" class = "flex items-center" >
< template # title > Generate text using AI is not supported when rich text formatting is enabled < / template >
< NcSwitch
v - model : checked = "isEnabledGenerateText"
: disabled = "richMode"
class = "nc-ai-field-generate-text nc-ai-input"
@ change = "handleDisableSubmitBtn"
>
< span
class = "text-sm font-semibold pl-1"
: class = " {
'text-nc-content-purple-dark' : isEnabledGenerateText ,
'text-nc-content-gray' : ! isEnabledGenerateText ,
} "
>
Generate text using AI
< / span >
< / NcSwitch >
< / NcTooltip >
< NcTooltip class = "ml-2 mr-[40px] flex cursor-pointer" >
< template # title > Use AI to generate content based on record data . < / template >
< GeneralIcon icon = "info" class = "text-nc-content-gray-muted hover:text-nc-content-gray-subtle opacity-70 w-3.5 h-3.5" / >
< / NcTooltip >
< div class = "flex-1" > < / div >
< div class = "absolute right-0" >
< AiSettings
v - model : fk - integration - id = "vModel.fk_integration_id"
v - model : model = "vModel.model"
v - model : randomness = "vModel.randomness"
: workspace - id = "activeWorkspaceId"
: show - tooltip = "false"
: is - edit - column = "isEdit"
placement = "bottomRight"
>
< NcButton size = "xs" theme = "ai" class = "!px-1" type = "text" >
< GeneralIcon icon = "settings" / >
< / NcButton >
< / AiSettings >
< / div >
< / a - f o r m - i t e m >
< / div >
< template v-if ="isPromptEnabled && (!isEdit ? aiIntegrationAvailable && isEnabledGenerateText : isEnabledGenerateText)" >
< a -form -item class = "flex" >
< div class = "nc-prompt-input-wrapper bg-nc-bg-gray-light rounded-lg w-full" >
< AiPromptWithFields
v - model = "vModel.prompt_raw"
: options = "availableFields"
: read - only = "!aiIntegrationAvailable"
placeholder = "Write custom AI Prompt instruction here"
prompt - field - tag - class - name = "!text-nc-content-purple-dark font-weight-500"
suggestion - icon - class - name = "!text-nc-content-purple-medium"
/ >
< div class = "rounded-b-lg flex items-center gap-1.5 p-1" >
< GeneralIcon icon = "info" class = "!text-nc-content-purple-medium w-3.5 h-3.5" / >
< span class = "text-xs text-nc-content-gray-subtle2"
> Mention fields using curly braces , e . g . < span class = "text-nc-content-purple-dark" > { Field name } < / span > . < / s p a n
>
< / div >
< / div >
< / a - f o r m - i t e m >
< div v-if ="aiIntegrationAvailable && isEnabledGenerateText" class="nc-ai-options-preview overflow-hidden" >
< div >
< div
class = "flex items-center gap-2 transition-all duration-300"
: class = " {
'pl-3 py-2 pr-2' : ! isAlreadyGenerated ,
'pl-3 py-1 pr-1 border-b-1 border-nc-border-gray-medium' : isAlreadyGenerated ,
} "
>
< div class = "flex flex-col flex-1 gap-1" >
< div class = "flex items-center gap-2" >
< span class = "text-sm font-bold text-nc-content-gray-subtle" > Preview < / span >
< NcTooltip class = "flex cursor-pointer" >
< template # title > Preview is generated using the first record in this table < / template >
< GeneralIcon
icon = "info"
class = "text-nc-content-gray-muted hover:text-nc-content-gray-subtle opacity-70 w-3.5 h-3.5"
/ >
< / NcTooltip >
< / div >
< span v-if ="!isAlreadyGenerated" class="text-[11px] leading-[18px] text-nc-content-gray-muted" >
Include at least 1 field in prompt .
< / span >
< / div >
< NcTooltip :disabled ="isPreviewEnabled" >
< template # title > Include at least 1 field in prompt to generate < / template >
< NcButton
class = "nc-aioptions-preview-generate-btn"
: class = " {
'nc-is-already-generated' : isAlreadyGenerated ,
'nc-preview-enabled' : isPreviewEnabled ,
} "
size = "xs"
: type = "isAlreadyGenerated ? 'text' : 'secondary'"
: theme = "isPreviewEnabled ? 'ai' : 'default'"
: disabled = "!isPreviewEnabled"
: loading = "generatingPreview"
@ click . stop = "generate"
>
< div
: class = " {
'nc-animate-dots min-w-[91px] text-left' : generatingPreview ,
'min-w-[102px]' : isAlreadyGenerated && generatingPreview ,
'min-w-[80px]' : ! isAlreadyGenerated && generatingPreview ,
} "
>
{ {
isAlreadyGenerated
? generatingPreview
? 'Re-generating'
: 'Re-generate'
: generatingPreview
? 'Generating'
: 'Generate preview'
} }
< / div >
< / NcButton >
< / NcTooltip >
< / div >
< div v-if ="previewRow.row?.[previewFieldTitle]?.value" >
< div class = "relative" >
< LazySmartsheetRow :row ="previewRow" >
< LazySmartsheetCell
: edit - enabled = "true"
: model - value = "previewRow.row[previewFieldTitle]"
: column = "vModel"
class = "!border-none h-auto my-auto pl-1"
/ >
< / LazySmartsheetRow >
< / div >
< / div >
< / NcSwitch >
< / div >
< / div >
< / a - f o r m - i t e m >
< / template >
< AiIntegrationNotFound v -if = " ! aiIntegrationAvailable & & isEnabledGenerateText & & isPromptEnabled " / >
< / div >
< / template >
< style lang = "scss" scoped >
: deep ( . ant - form - item - control - input - content ) {
@ apply flex items - center ;
}
. nc - prompt - input - wrapper {
@ apply border - 1 border - nc - border - gray - medium ;
box - shadow : 0 px 0 px 4 px 0 px rgba ( 0 , 0 , 0 , 0.08 ) ;
}
. nc - ai - options - preview {
@ apply rounded - lg border - 1 border - nc - border - gray - medium ;
box - shadow : 0 px 0 px 4 px 0 px rgba ( 0 , 0 , 0 , 0.08 ) ;
: deep ( . nc - text - area - expand - btn ) {
@ apply right - 1 ;
}
}
. nc - aioptions - preview - generate - btn {
& : not ( . nc - is - already - generated ) {
& . nc - preview - enabled {
@ apply ! border - transparent ;
}
}
}
< / style >