Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/client/packages/design-system/ads/src/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
ListItemTitleClassName,
} from "./List.constants";
import { useEventCallback } from "usehooks-ts";
import { Badge } from "../Badge";

function List({ children, className, groupTitle, ...rest }: ListProps) {
return groupTitle ? (
Expand Down Expand Up @@ -86,6 +87,7 @@ function ListItem(props: ListItemProps) {
hasError,
rightControl,
rightControlVisibility = "always",
showUnsavedChanges,
size = "md",
startIcon,
title,
Expand Down Expand Up @@ -159,6 +161,7 @@ function ListItem(props: ListItemProps) {
{rightControl}
</RightControlWrapper>
)}
{showUnsavedChanges ? <Badge kind="warning" /> : null}
</TopContentWrapper>
{isBlockDescription && (
<BottomContentWrapper data-isiconpresent={Boolean(startIcon)}>
Expand Down
2 changes: 2 additions & 0 deletions app/client/packages/design-system/ads/src/List/List.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface ListItemProps {
customTitleComponent?: ReactNode | ReactNode[];
/** dataTestId which will be used in automated tests */
dataTestId?: string;
/** Whether to show the unsaved changes indicator */
showUnsavedChanges?: boolean;
}

export interface ListProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EditableEntityName } from "../EditableEntityName";

import type { EditableDismissibleTabProps } from "./EditableDismissibleTab.types";
import { useActiveDoubleClick } from "../../__hooks__";
import { Badge } from "../../Badge";

export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
const {
Expand All @@ -22,6 +23,7 @@ export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
onEnterEditMode: propOnEnterEditMode,
onExitEditMode: propOnExitEditMode,
onNameSave,
showUnsavedChanges,
validateName,
} = props;

Expand Down Expand Up @@ -60,6 +62,7 @@ export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
onNameSave={onNameSave}
validateName={validateName}
/>
{showUnsavedChanges ? <Badge kind="warning" /> : null}
</DismissibleTab>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ export interface EditableDismissibleTabProps {
onNameSave: (name: string) => void;
/** Function to validate the name. */
validateName: (name: string) => string | null;
/** Show unsaved changes indicator. */
showUnsavedChanges?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as Styled from "./EditableEntityName.styles";

import type { EditableEntityNameProps } from "./EditableEntityName.types";
import clsx from "clsx";

export const isEllipsisActive = (element: HTMLElement | null) => {
return element && element.clientWidth < element.scrollWidth;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ListItemProps } from "../../../List";

export interface EntityItemProps
extends Omit<
ListItemProps,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { JSCollection } from "entities/JSCollection";

// Implementation exists in ee
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getHighlightedLines = (jsCollection: JSCollection): number[] => {
return [];
};
43 changes: 43 additions & 0 deletions app/client/src/ce/selectors/entitiesSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ export const getActions = (state: AppState): ActionDataState =>
export const getJSCollections = (state: AppState): JSCollectionDataState =>
state.entities.jsActions;

export const getAllJSCollectionActions = (state: AppState) => {
return state.entities.jsActions.flatMap(
(jsCollection) => jsCollection.config.actions,
);
};

export const getDatasource = (
state: AppState,
datasourceId: string,
Expand Down Expand Up @@ -1752,3 +1758,40 @@ export const getIsSavingEntityName = (

return isSavingEntityName;
};

export const getActionSchemaDirtyState = createSelector(
getAction,
(state: AppState) =>
getPluginByPackageName(state, PluginPackageName.APPSMITH_AI),
(action, agentPlugin) => {
if (!action) return false;

if (agentPlugin?.id === action.pluginId) {
return false;
}

return action.isDirtyMap?.SCHEMA_GENERATION;
},
);

export const getJSCollectionSchemaDirtyState = createSelector(
(state: AppState, collectionId: string) =>
getJSCollection(state, collectionId),
(jsCollection) => {
if (!jsCollection) return false;

return jsCollection.actions.some(
(action) => action.isDirtyMap?.SCHEMA_GENERATION,
);
},
);

export const getJSCollectionActionSchemaDirtyState = createSelector(
(state: AppState, collectionId: string, actionId: string) =>
getJSCollectionAction(state, collectionId, actionId),
(action) => {
if (!action) return false;

return action.isDirtyMap?.SCHEMA_GENERATION;
},
);
33 changes: 30 additions & 3 deletions app/client/src/components/editorComponents/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export type EditorProps = EditorStyleProps &
removeHoverAndFocusStyle?: boolean;

customErrors?: LintError[];
highlightedLines?: number[]; // Array of line numbers to highlight
};

interface Props extends ReduxStateProps, EditorProps, ReduxDispatchProps {}
Expand Down Expand Up @@ -445,11 +446,11 @@ class CodeEditor extends Component<Props, State> {
this: CodeEditor,
editor: CodeMirror.Editor,
) {
// If you need to do something with the editor right after its been created,
// If you need to do something with the editor right after it's been created,
// put that code here.
//
// This helps with performance: finishInit() is called inside
// CodeMirrors `operation()` (https://codemirror.net/doc/manual.html#operation
// CodeMirror's `operation()` (https://codemirror.net/doc/manual.html#operation
// which means CodeMirror recalculates itself only one time, once all CodeMirror
// changes here are completed
//
Expand Down Expand Up @@ -500,7 +501,13 @@ class CodeEditor extends Component<Props, State> {

// Finally create the Codemirror editor
this.editor = CodeMirror(this.codeEditorTarget.current, options);
// DO NOT ADD CODE BELOW. If you need to do something with the editor right after it’s created,

// Add highlighting for initial render
if (this.props.highlightedLines?.length) {
this.updateLineHighlighting(this.props.highlightedLines);
}

// DO NOT ADD CODE BELOW. If you need to do something with the editor right after it's created,
// put that code into `options.finishInit()`.
}

Expand Down Expand Up @@ -612,6 +619,11 @@ class CodeEditor extends Component<Props, State> {
}
}

// Check if highlighted lines have changed
if (!isEqual(prevProps.highlightedLines, this.props.highlightedLines)) {
this.updateLineHighlighting(this.props.highlightedLines || []);
}

this.editor.operation(() => {
const editorValue = this.editor.getValue();
// Safe update of value of the editor when value updated outside the editor
Expand Down Expand Up @@ -1634,6 +1646,21 @@ class CodeEditor extends Component<Props, State> {
this.editor.setValue(value);
};

// Add new method to handle line highlighting
private updateLineHighlighting = (lines: number[]) => {
// Clear existing highlights
for (let i = 0; i < this.editor.lineCount(); i++) {
this.editor.removeLineClass(i, "background", "highlighted-line");
}

// Add new highlights
lines.forEach((lineNumber) => {
if (lineNumber >= 0 && lineNumber < this.editor.lineCount()) {
this.editor.addLineClass(lineNumber, "background", "highlighted-line");
}
});
};

render() {
const {
border,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ export const EditorWrapper = styled.div<{
.CodeMirror-activeline-background {
background-color: #ececec;
}

.highlighted-line {
background-color: rgba(255, 255, 0, 0.2);
}
}
.CodeMirror-guttermarker-subtle {
color: var(--ads-v2-color-fg-subtle);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getHighlightedLines } from "ce/pages/Editor/JSEditor/utils/getHighlightedLines";
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React, { useCallback, useMemo } from "react";
import { EntityItem, EntityContextMenu } from "@appsmith/ads";
import type { AppState } from "ee/reducers";
import { getJsCollectionByBaseId } from "ee/selectors/entitiesSelector";
import {
getJsCollectionByBaseId,
getJSCollectionSchemaDirtyState,
} from "ee/selectors/entitiesSelector";
import { useDispatch, useSelector } from "react-redux";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
Expand Down Expand Up @@ -105,6 +108,10 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
validateName,
]);

const isJSActionSchemaDirty = useSelector((state: AppState) =>
getJSCollectionSchemaDirtyState(state, item.key),
);

return (
<EntityItem
className={clsx("t--jsaction", {
Expand All @@ -118,6 +125,7 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
onDoubleClick={() => enterEditMode(jsAction.id)}
rightControl={contextMenu}
rightControlVisibility="hover"
showUnsavedChanges={isJSActionSchemaDirty}
startIcon={JsFileIconV2(16, 16)}
title={item.title}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EntityItem, EntityContextMenu } from "@appsmith/ads";
import type { AppState } from "ee/reducers";
import {
getActionByBaseId,
getActionSchemaDirtyState,
getDatasource,
getPlugins,
} from "ee/selectors/entitiesSelector";
Expand Down Expand Up @@ -117,6 +118,10 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
validateName,
]);

const isActionSchemaDirty = useSelector((state: AppState) =>
getActionSchemaDirtyState(state, action.id),
);

return (
<EntityItem
className="action t--action-entity"
Expand All @@ -128,6 +133,7 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
onDoubleClick={() => enterEditMode(action.id)}
rightControl={contextMenu}
rightControlVisibility="hover"
showUnsavedChanges={isActionSchemaDirty}
startIcon={icon}
title={item.title}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { EditableDismissibleTab } from "@appsmith/ads";

import { type EntityItem } from "ee/IDE/Interfaces/EntityItem";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { getIsSavingEntityName } from "ee/selectors/entitiesSelector";
import {
getActionSchemaDirtyState,
getIsSavingEntityName,
getJSCollectionSchemaDirtyState,
} from "ee/selectors/entitiesSelector";

import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { sanitizeString } from "utils/URLUtils";
Expand Down Expand Up @@ -61,6 +65,16 @@ export function EditableTab(props: EditableTabProps) {
[dispatch, entity, id, segment],
);

const isJSActionSchemaDirty = useSelector((state) =>
getJSCollectionSchemaDirtyState(state, id),
);

const isActionSchemaDirty = useSelector((state) =>
getActionSchemaDirtyState(state, id),
);

const isSchemaDirty = isJSActionSchemaDirty || isActionSchemaDirty;

return (
<EditableDismissibleTab
dataTestId={`t--ide-tab-${sanitizeString(title)}`}
Expand All @@ -75,6 +89,7 @@ export function EditableTab(props: EditableTabProps) {
onEnterEditMode={enterEditMode}
onExitEditMode={exitEditMode}
onNameSave={handleNameSave}
showUnsavedChanges={isSchemaDirty}
validateName={validateName}
/>
);
Expand Down
4 changes: 4 additions & 0 deletions app/client/src/pages/Editor/JSEditor/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
getJSActionOption,
type OnUpdateSettingsProps,
} from "./JSEditorToolbar";
import { getHighlightedLines } from "ee/pages/Editor/JSEditor/utils/getHighlightedLines";

interface JSFormProps {
jsCollectionData: JSCollectionData;
Expand Down Expand Up @@ -287,6 +288,8 @@ function JSEditorForm({
}
};

const highlightedLines = getHighlightedLines(currentJSCollection);

useEffect(() => {
if (parseErrors.length || isEmpty(jsActions)) {
setDisableRunFunctionality(true);
Expand Down Expand Up @@ -373,6 +376,7 @@ function JSEditorForm({
currentJSCollection={currentJSCollection}
customGutter={JSGutters}
executing={isExecutingCurrentJSAction}
highlightedLines={highlightedLines}
onChange={handleEditorChange}
onUpdateSettings={onUpdateSettings}
onValueChange={(string) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Flex } from "@appsmith/ads";

interface Props {
executing: boolean;
highlightedLines?: number[];
onValueChange: (value: string) => void;
value: JSEditorTab;
showSettings: undefined | boolean;
Expand Down Expand Up @@ -44,6 +45,7 @@ export const JSEditorForm = (props: Props) => {
folding
height={"100%"}
hideEvaluatedValue
highlightedLines={props.highlightedLines}
input={{
value: props.currentJSCollection.body,
onChange: props.onChange,
Expand Down
1 change: 0 additions & 1 deletion app/client/src/pages/Editor/JSEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import AppJSEditorContextMenu from "./AppJSEditorContextMenu";
import { updateFunctionProperty } from "actions/jsPaneActions";
import type { OnUpdateSettingsProps } from "./JSEditorToolbar";
import { saveJSObjectName } from "actions/jsActionActions";

const LoadingContainer = styled(CenteredWrapper)`
height: 50%;
`;
Expand Down