Skip to content

Commit 5e7d7e6

Browse files
salevineclaude
andauthored
feat: land CE-safe Anvil revival changes ahead of EE sync (appsmith-ee#9229) (#41933)
## Description Ports the **CE-safe subset** of the EE "revive Anvil" PR (appsmith-ee#9229) into CE so the hourly CE→EE sync carries these shared-file changes upstream and the EE PR merges with fewer conflicts. > [!TIP] > **TL;DR:** All changes are gated behind Anvil feature flags or are inert-by-default. Existing classic (FIXED/AUTO) UI is unaffected. This is a sync-enablement change, not a user-facing feature toggle. **Highlights:** - **Widget loader registry split:** new `widgets/registry.ts` (SCC-free loader registry) populated via an `import "widgets"` side effect at bootstrap (`src/index.tsx`, eval worker, `test/testCommon.ts`). `EvaluationsSaga` and `EditorUtils` now resolve loaders from `widgets/registry`. The four EE-only WDS widget loaders are intentionally omitted from `widgets/index.ts`. - **Feature flags (additive, matches the EE PR as-is):** keep `release_anvil_enabled`, add `license_anvil_enabled` to `ce/entities/FeatureFlag.ts` and `FeatureFlagEnum.java`. - **Applications/Anvil wiring:** app-card-list wiring (`ce/pages/Applications`), `layoutSystemType` plumbing through `WorkspaceAction`/`ApplicationSagas`, `Severity.DEBUG` revival, `BindDataButton` `WDS_TABLE_WIDGET` case, and assorted shared-file changes applied byte-identical to EE for a clean sync. **Scope note:** EE-path files (`app/client/src/ee/**`) cannot be modified in a CE PR (enforced by the pre-push hook), so the new `ee/` stubs/mirrors and the five shared files that depend on them (`AppViewer`, `AppPage`, `Canvas`, `WDSThemePropertyPane`, `ApplicationCard`) are **not** included here. Those already exist in the EE PR; the CE-side `ee/` stubs arrive via the CE↔EE sync. **Also intentionally not ported:** design-system `Markdown.tsx`/`types.ts` (CE's Markdown is structurally divergent), `ee/widgets/wds/constants.ts` (EE-owned), and `cypress_ci_custom.config.ts` (divergent CI tuning). **Verification:** client `check-types`, ESLint, Prettier, and server `spotless:check` all pass on the included files. Related: appsmith-ee#9229 > [!NOTE] > No standalone CE issue — this PR exists to keep the CE↔EE shared-file sync clean ahead of the EE Anvil revival merge. ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/28452962986> > Commit: e3c729b > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=28452962986&attempt=3" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Wed, 01 Jul 2026 14:51:09 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for Anvil apps in the Applications page, including a new section label, empty state, and create-new-app flow. * Expanded widget support so more WDS widgets appear in the editor and can be bound more consistently. * Added a new debug severity display in the console and debugger. * **Bug Fixes** * Improved feature-flag handling so selected flags can be merged without losing other existing flags. * Updated layout handling so Anvil-specific behavior is only shown when the required flags are enabled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f247280 commit 5e7d7e6

24 files changed

Lines changed: 303 additions & 94 deletions

File tree

app/client/cypress/support/Objects/FeatureFlags.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,50 @@ const defaultFlags = {
1111
export const featureFlagIntercept = (
1212
flags: Record<string, boolean> = {},
1313
reload = true,
14+
// When true, the requested flags are OVERLAID on the real feature flags from the
15+
// backend instead of REPLACING them. The replace behavior drops every flag the test
16+
// didn't list — including editor-infrastructure flags (e.g. release_app_sidebar_enabled)
17+
// that the IDE needs to render at all. Tests that open the full editor (Anvil/AI) must
18+
// preserve those, or the editor never mounts (.t--sidebar-Editor never appears).
19+
preserveOtherFlags = false,
1420
) => {
15-
getConsolidatedDataApi({ ...flags, ...defaultFlags }, false);
16-
const response = {
17-
responseMeta: {
18-
status: 200,
19-
success: true,
20-
},
21-
data: {
22-
...flags,
23-
...defaultFlags,
24-
},
25-
errorDisplay: "",
26-
};
27-
cy.intercept("GET", "/api/v1/users/features", response);
21+
getConsolidatedDataApi(
22+
{ ...flags, ...defaultFlags },
23+
false,
24+
preserveOtherFlags,
25+
);
26+
if (preserveOtherFlags) {
27+
cy.intercept("GET", "/api/v1/users/features", (req) => {
28+
req.reply((res: any) => {
29+
const original = res?.body?.data ?? {};
30+
res.send({
31+
responseMeta: { status: 200, success: true },
32+
data: { ...original, ...flags, ...defaultFlags },
33+
errorDisplay: "",
34+
});
35+
});
36+
});
37+
} else {
38+
const response = {
39+
responseMeta: {
40+
status: 200,
41+
success: true,
42+
},
43+
data: {
44+
...flags,
45+
...defaultFlags,
46+
},
47+
errorDisplay: "",
48+
};
49+
cy.intercept("GET", "/api/v1/users/features", response);
50+
}
2851
if (reload) ObjectsRegistry.AggregateHelper.CypressReload();
2952
};
3053

3154
export const getConsolidatedDataApi = (
3255
flags: Record<string, boolean> = {},
3356
reload = true,
57+
preserveOtherFlags = false,
3458
) => {
3559
cy.intercept("GET", "/api/v1/consolidated-api/*?*", (req) => {
3660
delete req.headers["if-none-match"];
@@ -43,7 +67,9 @@ export const getConsolidatedDataApi = (
4367
const originalResponse = res?.body;
4468
try {
4569
const updatedResponse = JSON.parse(JSON.stringify(originalResponse));
46-
updatedResponse.data.featureFlags.data = { ...flags };
70+
updatedResponse.data.featureFlags.data = preserveOtherFlags
71+
? { ...updatedResponse.data.featureFlags.data, ...flags }
72+
: { ...flags };
4773
return res.send(updatedResponse);
4874
} catch (e) {
4975
cy.log(`Featureflags.ts error `, e);

app/client/src/PluginActionEditor/components/PluginActionResponse/components/BindDataButton.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ function getWidgetProps(
160160
parentRowSpace: 10,
161161
};
162162
case "TABLE_WIDGET_V2":
163+
case "WDS_TABLE_WIDGET":
163164
return {
164-
type: "TABLE_WIDGET_V2",
165+
type: suggestedWidget.type,
165166
props: {
166167
[fieldName]: `{{${actionName}.${suggestedWidget.bindingQuery}}}`,
167168
dynamicBindingPathList: [{ key: "tableData" }],
@@ -276,11 +277,16 @@ function BindDataButton(props: BindDataButtonProps) {
276277
const isAnvilLayout = useSelector(getIsAnvilLayout);
277278
// The purpose of this filter is to make sure that if Anvil is enabled
278279
// only those widgets which have an alternative in Anvil are listed
279-
// for selection for adding a new suggested widget
280+
// for selection for adding a new suggested widget. A suggestion survives
281+
// the filter if it is a legacy type with a WDS counterpart (a map key,
282+
// e.g. TABLE_WIDGET_V2) or already a WDS type (a map value, e.g.
283+
// WDS_TABLE_WIDGET).
280284
const filteredSuggestedWidgets =
281285
isAnvilLayout && suggestedWidgets
282-
? suggestedWidgets.filter((each) =>
283-
Object.keys(WDS_V2_WIDGET_MAP).includes(each.type),
286+
? suggestedWidgets.filter(
287+
(each) =>
288+
Object.keys(WDS_V2_WIDGET_MAP).includes(each.type) ||
289+
Object.values(WDS_V2_WIDGET_MAP).includes(each.type),
284290
)
285291
: suggestedWidgets;
286292

app/client/src/ce/constants/messages.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ export const FIXED_APPLICATIONS = () => `Classic Applications`;
219219
export const AI_AGENTS_APPLICATIONS = () => `AI Agents`;
220220
export const AI_APPLICATION_CARD_LIST_ZERO_STATE = () =>
221221
`There are no AI Agents in this workspace.`;
222+
export const ANVIL_APPLICATIONS = () => `Anvil apps`;
223+
export const ANVIL_APPLICATION_CARD_LIST_ZERO_STATE = () =>
224+
`There are no Anvil apps in this workspace yet.`;
225+
export const NEW_ANVIL_APP = () => `Anvil app`;
222226
export const AI_AGENT_AUTH_SUBTITLE = () =>
223227
`Sign up with any Google account.\n Support for email will be available soon.`;
224228

app/client/src/ce/entities/FeatureFlag.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const FEATURE_FLAG = {
1717
"release_show_publish_app_to_community_enabled",
1818
license_gac_enabled: "license_gac_enabled",
1919
release_anvil_enabled: "release_anvil_enabled",
20+
license_anvil_enabled: "license_anvil_enabled",
2021
license_ai_agent_enabled: "license_ai_agent_enabled",
2122
license_git_branch_protection_enabled:
2223
"license_git_branch_protection_enabled",
@@ -87,6 +88,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
8788
release_show_publish_app_to_community_enabled: false,
8889
license_gac_enabled: false,
8990
release_anvil_enabled: false,
91+
license_anvil_enabled: false,
9092
license_ai_agent_enabled: false,
9193
release_drag_drop_building_blocks_enabled: false,
9294
license_git_branch_protection_enabled: false,

app/client/src/ce/pages/Applications/WorkspaceAction.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
createMessage,
1818
} from "ee/constants/messages";
1919
import type { Workspace } from "ee/constants/workspaceConstants";
20+
import type { LayoutSystemTypes } from "layoutSystems/types";
2021
import { getIsCreatingApplicationByWorkspaceId } from "ee/selectors/applicationSelectors";
2122
import { getIsFetchingApplications } from "ee/selectors/selectedWorkspaceSelectors";
2223
import { hasCreateNewAppPermission } from "ee/utils/permissionHelpers";
@@ -27,7 +28,10 @@ export interface WorkspaceActionProps {
2728
isMobile: boolean;
2829
enableImportExport: boolean;
2930
workspaceId: string;
30-
onCreateNewApplication: (workspaceId: string) => void;
31+
onCreateNewApplication: (
32+
workspaceId: string,
33+
layoutSystemType?: LayoutSystemTypes,
34+
) => void;
3135
onStartFromTemplate: (workspaceId: string) => void;
3236
setSelectedWorkspaceIdForImportApplication: (workspaceId?: string) => void;
3337
}

app/client/src/ce/pages/Applications/index.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import type { UpdateApplicationPayload } from "ee/api/ApplicationApi";
1010
import {
1111
AI_AGENTS_APPLICATIONS,
1212
AI_APPLICATION_CARD_LIST_ZERO_STATE,
13+
ANVIL_APPLICATIONS,
14+
ANVIL_APPLICATION_CARD_LIST_ZERO_STATE,
1315
APPLICATIONS,
1416
CREATE_A_NEW_WORKSPACE,
1517
createMessage,
@@ -22,6 +24,7 @@ import {
2224
getIsAiAgentFlowEnabled,
2325
getIsAiAgentInstanceEnabled,
2426
} from "ee/selectors/aiAgentSelectors";
27+
import { getIsAnvilLayoutEnabled } from "layoutSystems/anvil/integrations/selectors";
2528
import type { ApplicationPayload } from "entities/Application";
2629
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
2730
import { createWorkspaceSubmitHandler } from "ee/pages/workspace/helpers";
@@ -709,6 +712,7 @@ export function ApplicationsSection(props: any) {
709712
const creatingApplicationMap = useSelector(getIsCreatingApplication);
710713
const isAiAgentFlowEnabled = useSelector(getIsAiAgentFlowEnabled);
711714
const isAiAgentInstanceEnabled = useSelector(getIsAiAgentInstanceEnabled);
715+
const isAnvilEnabled = useSelector(getIsAnvilLayoutEnabled);
712716
const currentUser = useSelector(getCurrentUser);
713717
const isMobile = useIsMobileDevice();
714718
const urlParams = new URLSearchParams(location.search);
@@ -832,6 +836,7 @@ export function ApplicationsSection(props: any) {
832836
const createNewApplication = (
833837
applicationName: string,
834838
workspaceId: string,
839+
layoutSystemType?: LayoutSystemTypes,
835840
) => {
836841
const color = getRandomPaletteColor(theme.colors.appCardColors);
837842
const icon =
@@ -844,6 +849,7 @@ export function ApplicationsSection(props: any) {
844849
workspaceId,
845850
icon,
846851
color,
852+
layoutSystemType,
847853
},
848854
});
849855
};
@@ -918,7 +924,10 @@ export function ApplicationsSection(props: any) {
918924
isManageEnvironmentEnabled &&
919925
hasManageWorkspaceEnvironmentPermission(activeWorkspace.userPermissions);
920926

921-
const onClickAddNewAppButton = (workspaceId: string) => {
927+
const onClickAddNewAppButton = (
928+
workspaceId: string,
929+
layoutSystemType?: LayoutSystemTypes,
930+
) => {
922931
if (
923932
Object.entries(creatingApplicationMap).length === 0 ||
924933
(creatingApplicationMap && !creatingApplicationMap[workspaceId])
@@ -931,6 +940,7 @@ export function ApplicationsSection(props: any) {
931940
applications.map((el: any) => el.name),
932941
),
933942
workspaceId,
943+
layoutSystemType,
934944
);
935945
}
936946
};
@@ -1073,15 +1083,15 @@ export function ApplicationsSection(props: any) {
10731083
workspaceId={activeWorkspace.id}
10741084
/>
10751085
)}
1076-
{isAiAgentFlowEnabled && (
1086+
{(isAiAgentFlowEnabled || isAnvilEnabled) && (
10771087
<ApplicationCardList
10781088
applications={anvilApplications}
10791089
canInviteToWorkspace={canInviteToWorkspace}
10801090
deleteApplication={deleteApplication}
10811091
emptyStateMessage={
10821092
isAiAgentFlowEnabled
10831093
? createMessage(AI_APPLICATION_CARD_LIST_ZERO_STATE)
1084-
: undefined
1094+
: createMessage(ANVIL_APPLICATION_CARD_LIST_ZERO_STATE)
10851095
}
10861096
enableImportExport={enableImportExport}
10871097
hasCreateNewApplicationPermission={
@@ -1090,7 +1100,11 @@ export function ApplicationsSection(props: any) {
10901100
hasManageWorkspacePermissions={hasManageWorkspacePermissions}
10911101
isMobile={isMobile}
10921102
onClickAddNewButton={onClickAddNewAppButton}
1093-
title={createMessage(AI_AGENTS_APPLICATIONS)}
1103+
title={createMessage(
1104+
isAiAgentFlowEnabled
1105+
? AI_AGENTS_APPLICATIONS
1106+
: ANVIL_APPLICATIONS,
1107+
)}
10941108
titleTag={PreviewTag}
10951109
updateApplicationDispatch={updateApplicationDispatch}
10961110
workspaceId={activeWorkspace.id}

app/client/src/ce/sagas/ApplicationSagas.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ export function* createApplicationSaga(
648648
icon: IconNames;
649649
color: AppColorCode;
650650
workspaceId: string;
651+
layoutSystemType?: LayoutSystemTypes;
651652
// TODO: Fix this the next time the file is edited
652653
// eslint-disable-next-line @typescript-eslint/no-explicit-any
653654
resolve: any;
@@ -656,7 +657,14 @@ export function* createApplicationSaga(
656657
reject: any;
657658
}>,
658659
) {
659-
const { applicationName, color, icon, reject, workspaceId } = action.payload;
660+
const {
661+
applicationName,
662+
color,
663+
icon,
664+
layoutSystemType,
665+
reject,
666+
workspaceId,
667+
} = action.payload;
660668

661669
try {
662670
const applications: Workspace[] = yield select(getApplicationsOfWorkspace);
@@ -683,7 +691,8 @@ export function* createApplicationSaga(
683691
icon: icon,
684692
color: color,
685693
workspaceId,
686-
layoutSystemType: LayoutSystemTypes.FIXED, // Note: This may be provided as an action payload in the future
694+
// Defaults to FIXED; callers (e.g. the Anvil create option) may request ANVIL.
695+
layoutSystemType: layoutSystemType ?? LayoutSystemTypes.FIXED,
687696
};
688697

689698
const response: CreateApplicationResponse = yield call(

app/client/src/components/editorComponents/Debugger/helpers.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export function BlankState(props: {
5252
}
5353

5454
export const SeverityIcon: Record<Severity, string> = {
55+
[Severity.DEBUG]: "bug-line",
5556
[Severity.INFO]: "success",
5657
[Severity.ERROR]: "close-circle",
5758
[Severity.WARNING]: "warning",

app/client/src/entities/AppsmithConsole/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ export interface LogObject {
3535
export type ErrorType = PropertyEvaluationErrorType | PLATFORM_ERROR;
3636

3737
export enum Severity {
38-
// Everything, irrespective of what the user should see or not
39-
// DEBUG = "debug",
38+
// Verbose diagnostic detail; only surfaced when debug logging is enabled.
39+
// Revived for Anvil level-based logging (REC-12); additive, opt-in.
40+
DEBUG = "debug",
4041
// Something the dev user should probably know about
4142
INFO = "info",
4243
// Doesn't break the app, but can cause slowdowns / ux issues/ unexpected behaviour

app/client/src/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import "./preload-route-chunks";
33
// Initialise eval worker instance
44
import "utils/workerInstances";
5+
// Populate the widget loader registry (side effect of importing the barrel).
6+
// Consumers (EvaluationsSaga, EditorUtils) read loaders from `widgets/registry`,
7+
// so this import is what registers them on the main thread.
8+
import "widgets";
59

610
import React from "react";
711
import "./wdyr";

0 commit comments

Comments
 (0)