diff --git a/app/client/src/ce/components/editorComponents/CodeEditorSignPosting.tsx b/app/client/src/ce/components/editorComponents/CodeEditorSignPosting.tsx
new file mode 100644
index 0000000000..3ca8326c9c
--- /dev/null
+++ b/app/client/src/ce/components/editorComponents/CodeEditorSignPosting.tsx
@@ -0,0 +1,26 @@
+import BindingPrompt from "components/editorComponents/CodeEditor/BindingPrompt";
+import type {
+ EditorTheme,
+ TEditorModes,
+} from "components/editorComponents/CodeEditor/EditorConfig";
+import React from "react";
+
+export function CodeEditorSignPosting(props: {
+ promptMessage?: React.ReactNode | string;
+ isOpen?: boolean;
+ editorTheme?: EditorTheme;
+ showLightningMenu?: boolean;
+ isAIEnabled?: boolean;
+ mode: TEditorModes;
+ forComp?: string;
+}): JSX.Element {
+ return (
+
+ );
+}
diff --git a/app/client/src/ce/components/editorComponents/EditorFormSignPosting.tsx b/app/client/src/ce/components/editorComponents/EditorFormSignPosting.tsx
new file mode 100644
index 0000000000..bc54978c45
--- /dev/null
+++ b/app/client/src/ce/components/editorComponents/EditorFormSignPosting.tsx
@@ -0,0 +1,11 @@
+import type { TEditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
+
+export type Props = {
+ isAIEnabled: boolean;
+ mode: TEditorModes;
+};
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export function EditorFormSignPosting(props: Props) {
+ return null;
+}
diff --git a/app/client/src/ce/components/editorComponents/GPT/index.tsx b/app/client/src/ce/components/editorComponents/GPT/index.tsx
index 2c5e5f816c..2669630fa5 100644
--- a/app/client/src/ce/components/editorComponents/GPT/index.tsx
+++ b/app/client/src/ce/components/editorComponents/GPT/index.tsx
@@ -1,5 +1,8 @@
import type { CodeEditorExpected } from "components/editorComponents/CodeEditor";
-import type { TEditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
+import type {
+ FieldEntityInformation,
+ TEditorModes,
+} from "components/editorComponents/CodeEditor/EditorConfig";
import React from "react";
export type TAIWrapperProps = {
@@ -12,6 +15,7 @@ export type TAIWrapperProps = {
enableAIAssistance: boolean;
dataTreePath?: string;
mode: TEditorModes;
+ entity: FieldEntityInformation;
};
export function AIWindow(props: TAIWrapperProps) {
diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx
index f99c25f734..f982830edd 100644
--- a/app/client/src/ce/constants/ReduxActionConstants.tsx
+++ b/app/client/src/ce/constants/ReduxActionConstants.tsx
@@ -834,6 +834,7 @@ const ActionTypes = {
BIND_WIDGET_TO_DATASOURCE_ERROR: "BIND_WIDGET_TO_DATASOURCE_ERROR",
LOAD_FILE_PICKER_ACTION: "LOAD_FILE_PICKER_ACTION",
TOGGLE_AI_WINDOW: "TOGGLE_AI_WINDOW",
+ UPDATE_AI_TRIGGERED: "UPDATE_AI_TRIGGERED",
UPDATE_DATASOURCE_AUTH_STATE: "UPDATE_DATASOURCE_AUTH_STATE",
UPDATE_POSITIONS_ON_TAB_CHANGE: "UPDATE_POSITIONS_ON_TAB_CHANGE",
RESET_DATA_TREE: "RESET_DATA_TREE",
diff --git a/app/client/src/components/designSystems/appsmith/SignPostingBanner.tsx b/app/client/src/components/designSystems/appsmith/SignPostingBanner.tsx
new file mode 100644
index 0000000000..ad85380950
--- /dev/null
+++ b/app/client/src/components/designSystems/appsmith/SignPostingBanner.tsx
@@ -0,0 +1,33 @@
+import React from "react";
+import type { ReactNode } from "react";
+import styled from "styled-components";
+import { Icon } from "design-system";
+
+export type SignPostingBannerProps = {
+ iconName: string;
+ content: ReactNode;
+};
+
+export const Container = styled.div`
+ background-color: var(--ads-v2-color-blue-100);
+ width: 100%;
+ display: flex;
+`;
+
+function SignPostingBanner(props: SignPostingBannerProps) {
+ return (
+
+
+
+
+ {props.content}
+
+ );
+}
+
+export default SignPostingBanner;
diff --git a/app/client/src/components/editorComponents/CodeEditor/EditorConfig.ts b/app/client/src/components/editorComponents/CodeEditor/EditorConfig.ts
index 1894fa20e4..99f82d2181 100644
--- a/app/client/src/components/editorComponents/CodeEditor/EditorConfig.ts
+++ b/app/client/src/components/editorComponents/CodeEditor/EditorConfig.ts
@@ -6,6 +6,7 @@ import type { EntityNavigationData } from "selectors/navigationSelectors";
import type { ExpectedValueExample } from "utils/validation/common";
import { editorSQLModes } from "./sql/config";
+import type { WidgetType } from "constants/WidgetConstants";
export const EditorModes = {
TEXT: "text/plain",
@@ -61,6 +62,7 @@ export type FieldEntityInformation = {
example?: ExpectedValueExample;
mode?: TEditorModes;
token?: CodeMirror.Token;
+ widgetType?: WidgetType;
};
export type HintHelper = (
diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx
index 2a91e694d2..2c15b853a0 100644
--- a/app/client/src/components/editorComponents/CodeEditor/index.tsx
+++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx
@@ -71,7 +71,7 @@ import {
bindingHint,
sqlHint,
} from "components/editorComponents/CodeEditor/hintHelpers";
-import BindingPrompt from "./BindingPrompt";
+
import { showBindingPrompt } from "./BindingPromptHelper";
import { Button } from "design-system";
import "codemirror/addon/fold/brace-fold";
@@ -103,6 +103,7 @@ import { getLintAnnotations, getLintTooltipDirection } from "./lintHelpers";
import { executeCommandAction } from "actions/apiPaneActions";
import { startingEntityUpdate } from "actions/editorActions";
import type { SlashCommandPayload } from "entities/Action";
+import { SlashCommand } from "entities/Action";
import type { Indices } from "constants/Layers";
import { replayHighlightClass } from "globalStyles/portals";
import {
@@ -158,6 +159,7 @@ import { PeekOverlayExpressionIdentifier, SourceType } from "@shared/ast";
import type { MultiplexingModeConfig } from "components/editorComponents/CodeEditor/modes";
import { MULTIPLEXING_MODE_CONFIGS } from "components/editorComponents/CodeEditor/modes";
import { getDeleteLineShortcut } from "./utils/deleteLine";
+import { CodeEditorSignPosting } from "@appsmith/components/editorComponents/CodeEditorSignPosting";
type ReduxStateProps = ReturnType;
type ReduxDispatchProps = ReturnType;
@@ -489,6 +491,10 @@ class CodeEditor extends Component {
handleSlashCommandSelection = (...args: any) => {
const [command] = args;
if (command === APPSMITH_AI) {
+ this.props.executeCommand({
+ actionType: SlashCommand.ASK_AI,
+ args: {},
+ });
this.setState({ showAIWindow: true });
}
this.handleAutocompleteVisibility(this.editor);
@@ -1244,6 +1250,8 @@ class CodeEditor extends Component {
entityInformation.entityId = entity.widgetId;
if (isTriggerPath)
entityInformation.expectedType = AutocompleteDataType.FUNCTION;
+
+ entityInformation.widgetType = entity.type;
}
}
entityInformation.propertyPath = propertyPath;
@@ -1552,12 +1560,14 @@ class CodeEditor extends Component {
currentValue={this.props.input.value}
dataTreePath={dataTreePath}
enableAIAssistance={this.AIEnabled}
+ entity={entityInformation}
isOpen={this.state.showAIWindow}
mode={this.props.mode}
triggerContext={this.props.expected}
update={this.updateValueWithAIResponse}
>
{
isNotHover={this.state.isFocused || this.state.isOpened}
isRawView={this.props.isRawView}
isReadOnly={this.props.isReadOnly}
+ mode={this.props.mode}
onMouseMove={this.handleLintTooltip}
onMouseOver={this.handleMouseMove}
ref={this.editorWrapperRef}
@@ -1604,10 +1615,12 @@ class CodeEditor extends Component {
ref={this.codeEditorTarget}
tabIndex={0}
>
-
diff --git a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
index 6ec052c0fa..72e70e3d5e 100644
--- a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
+++ b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
@@ -1,5 +1,6 @@
import styled from "styled-components";
import type { CodeEditorBorder } from "components/editorComponents/CodeEditor/EditorConfig";
+
import {
EditorSize,
EditorTheme,
@@ -56,6 +57,8 @@ export const EditorWrapper = styled.div<{
codeEditorVisibleOverflow?: boolean;
ctrlPressed: boolean;
removeHoverAndFocusStyle?: boolean;
+ AIEnabled?: boolean;
+ mode: string;
}>`
// Bottom border was getting clipped
.CodeMirror.cm-s-duotone-light.CodeMirror-wrap {
diff --git a/app/client/src/ee/components/editorComponents/CodeEditorSignPosting.tsx b/app/client/src/ee/components/editorComponents/CodeEditorSignPosting.tsx
new file mode 100644
index 0000000000..117f8b0f6f
--- /dev/null
+++ b/app/client/src/ee/components/editorComponents/CodeEditorSignPosting.tsx
@@ -0,0 +1 @@
+export * from "ce/components/editorComponents/CodeEditorSignPosting";
diff --git a/app/client/src/ee/components/editorComponents/EditorFormSignPosting.tsx b/app/client/src/ee/components/editorComponents/EditorFormSignPosting.tsx
new file mode 100644
index 0000000000..62e1e3d375
--- /dev/null
+++ b/app/client/src/ee/components/editorComponents/EditorFormSignPosting.tsx
@@ -0,0 +1 @@
+export * from "ce/components/editorComponents/EditorFormSignPosting";
diff --git a/app/client/src/entities/Engine/AppEditorEngine.ts b/app/client/src/entities/Engine/AppEditorEngine.ts
index 8d4e4031d8..22657a827e 100644
--- a/app/client/src/entities/Engine/AppEditorEngine.ts
+++ b/app/client/src/entities/Engine/AppEditorEngine.ts
@@ -58,6 +58,7 @@ import {
} from "@appsmith/sagas/userSagas";
import { getFirstTimeUserOnboardingComplete } from "selectors/onboardingSelectors";
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
+import { getAIPromptTriggered } from "utils/storage";
export default class AppEditorEngine extends AppEngine {
constructor(mode: APP_MODE) {
@@ -214,6 +215,16 @@ export default class AppEditorEngine extends AppEngine {
payload: [],
});
}
+
+ const noOfTimesAIPromptTriggered: number = yield getAIPromptTriggered();
+
+ yield put({
+ type: ReduxActionTypes.UPDATE_AI_TRIGGERED,
+ payload: {
+ value: noOfTimesAIPromptTriggered,
+ },
+ });
+
yield call(waitForWidgetConfigBuild);
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
index dcbbfa588c..bc32098e2c 100644
--- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
+++ b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
@@ -131,8 +131,14 @@ import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "entities/AppsmithConsole";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs";
+import { isAIEnabled } from "@appsmith/components/editorComponents/GPT/trigger";
+import { editorSQLModes } from "components/editorComponents/CodeEditor/sql/config";
+import { EditorFormSignPosting } from "@appsmith/components/editorComponents/EditorFormSignPosting";
import { DatasourceStructureContext } from "../Explorer/Datasources/DatasourceStructureContainer";
-import { selectFeatureFlagCheck } from "@appsmith/selectors/featureFlagsSelectors";
+import {
+ selectFeatureFlagCheck,
+ selectFeatureFlags,
+} from "@appsmith/selectors/featureFlagsSelectors";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
const QueryFormContainer = styled.form`
@@ -875,6 +881,14 @@ export function EditorJSONtoForm(props: Props) {
dispatch(setDebuggerSelectedTab(tabKey));
}, []);
+ const featureFlags = useSelector(selectFeatureFlags);
+ const editorMode =
+ currentActionPluginName === PluginName.POSTGRES
+ ? editorSQLModes.POSTGRESQL_WITH_BINDING
+ : editorSQLModes.MYSQL_WITH_BINDING;
+
+ const isAIEnabledForPosting = isAIEnabled(featureFlags, editorMode);
+
// close the debugger
//TODO: move this to a common place
const onClose = () => dispatch(showDebugger(false));
@@ -980,6 +994,11 @@ export function EditorJSONtoForm(props: Props) {
className="tab-panel"
value={EDITOR_TABS.QUERY}
>
+
+
{editorConfig && editorConfig.length > 0 ? (
renderConfig(editorConfig)
diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts
index 50048056b4..fccfaaf893 100644
--- a/app/client/src/sagas/ActionSagas.ts
+++ b/app/client/src/sagas/ActionSagas.ts
@@ -127,6 +127,7 @@ import { DEFAULT_GRAPHQL_ACTION_CONFIG } from "constants/ApiEditorConstants/Grap
import { DEFAULT_API_ACTION_CONFIG } from "constants/ApiEditorConstants/ApiEditorConstants";
import { createNewApiName, createNewQueryName } from "utils/AppsmithUtils";
import { fetchDatasourceStructure } from "actions/datasourceActions";
+import { setAIPromptTriggered } from "utils/storage";
export function* createDefaultActionPayload(
pageId: string,
@@ -942,6 +943,21 @@ function* executeCommandSaga(actionPayload: ReduxAction) {
break;
case SlashCommand.ASK_AI: {
const context = get(actionPayload, "payload.args", {});
+
+ const noOfTimesAIPromptTriggered: number = yield select(
+ (state) => state.ai.noOfTimesAITriggered,
+ );
+
+ if (noOfTimesAIPromptTriggered < 5) {
+ const currentValue: number = yield setAIPromptTriggered();
+ yield put({
+ type: ReduxActionTypes.UPDATE_AI_TRIGGERED,
+ payload: {
+ value: currentValue,
+ },
+ });
+ }
+
yield put({
type: ReduxActionTypes.TOGGLE_AI_WINDOW,
payload: {
diff --git a/app/client/src/utils/storage.ts b/app/client/src/utils/storage.ts
index b53e7c5b36..0098a2d57b 100644
--- a/app/client/src/utils/storage.ts
+++ b/app/client/src/utils/storage.ts
@@ -23,6 +23,7 @@ export const STORAGE_KEYS: {
FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY:
"FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY",
SIGNPOSTING_APP_STATE: "SIGNPOSTING_APP_STATE",
+ AI_TRIGGERED: "AI_TRIGGERED",
FEATURE_WALKTHROUGH: "FEATURE_WALKTHROUGH",
USER_SIGN_UP: "USER_SIGN_UP",
};
@@ -421,6 +422,39 @@ export const setFirstTimeUserOnboardingTelemetryCalloutVisibility = async (
}
};
+export const setAIPromptTriggered = async () => {
+ try {
+ let noOfTimesAITriggered: number = await getAIPromptTriggered();
+
+ if (noOfTimesAITriggered >= 5) {
+ return noOfTimesAITriggered;
+ }
+
+ noOfTimesAITriggered += 1;
+ await store.setItem(STORAGE_KEYS.AI_TRIGGERED, noOfTimesAITriggered);
+
+ return noOfTimesAITriggered;
+ } catch (error) {
+ log.error("An error occurred while setting AI_TRIGGERED");
+ log.error(error);
+
+ return 0;
+ }
+};
+
+export const getAIPromptTriggered = async () => {
+ try {
+ const flag: number | null = await store.getItem(STORAGE_KEYS.AI_TRIGGERED);
+
+ if (flag === null) return 0;
+
+ return flag;
+ } catch (error) {
+ log.error("An error occurred while fetching AI_TRIGGERED");
+ log.error(error);
+ return 0;
+ }
+};
export const setFeatureFlagShownStatus = async (key: string, value: any) => {
try {
let flagsJSON: Record | null = await store.getItem(